美文网首页
vue3 全局弹窗组件编写

vue3 全局弹窗组件编写

作者: 咕嘟咕嘟li | 来源:发表于2023-03-27 17:02 被阅读0次
    弹窗示例 弹窗示例
    alert1 alert2
    alert3 alert4
    1. 用命令行npm init vue@latest新建的项目,并运行起来
    2. components文件夹下新建一个文件夹hl-alert,该文件夹下新增hl-alert.vue和index.js文件,目录结构如下图


      目录结构

      2.1 hl-alet.vue文件中定义传入组件的属性和方法,内容如下:

    <template>
      <div class="base-alert" ref="hlAlert">
        <div class="base-alert-bg"></div>
        <div class="m-pop-content">
          <img src="~@/assets/close.png" alt="" class="m-pop-close" v-if="isCloseIcon" @click="handleCancel"/>
          <div class="m-pop-title" v-html="title" v-if="title"></div>
          <div class="m-pop-detail">
            <div v-html="message" v-if="message" class="m-pop-message"></div>
            <div v-html="subMessage" v-if="subMessage" class="m-pop-submessage"></div>
          </div>
          <div class="g-row-flex-right g-btn-group">
            <button :class="['u-base-pop-cancel', cancelBtnClass]" @click="handleCancel" v-if="!hideCancel">{{cancelBtnText}}</button>
            <button class="u-base-pop-create" @click="handleOk">{{ensureBtnText}}</button>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import { defineComponent, onMounted, ref } from 'vue'
    export default defineComponent({
      name: 'HlAlert',
      props: {
        title: {
          type: String,
          default: '信息提示',
        },
        message: {
          type: String,
          default: '',
        },
        subMessage: {
          type: String,
          default: '',
        },
        cancelBtnClass: {
          type: String,
          default: '',
        },
        ensureBtnText: {
          type: String,
          default: '确定',
        },
        cancelBtnText: {
          type: String,
          default: '取消',
        },
        isCloseIcon: {
          type: Boolean,
          default: false,
        },
        callback: {
          type: Function,
        },
        callbackCancel: {
          type: Function,
        },
        hideCancel: {
          type: Boolean,
          default: false,
        },
        closeFromWindowClick: {
          type: Boolean,
          default: true
        }
      },
      setup (props) {
        const hlAlert = ref(null)
        function removeModal () {
          props.closeFromWindowClick && window.removeEventListener('click', removeModal)
          let parent = (hlAlert.value && (hlAlert.value).parentNode) || null
          if (hlAlert.value && document.body.contains(parent)) {
            document.body.removeChild(parent)
          }
        }
        function handleCancel () {
          removeModal()
          props.callbackCancel && props.callbackCancel()
        }
        function handleOk () {
          removeModal()
          props.callback && props.callback()
        }
        onMounted(() => {
          setTimeout(() => {
            props.closeFromWindowClick && window.addEventListener('click', removeModal)
          }, 100)
        })
        return {
          hlAlert,
          handleOk,
          handleCancel
        }
      }
    })
    </script>
    
    <style lang="scss">
      @mixin box-sizing($value) {
        box-sizing: $value;
        -webkit-box-sizing: $value;
        -o-box-sizing: $value;
        -moz-box-sizing: $value;
      }
    
      @mixin box($w, $h) {
        height: $h;
        width: $w;
        padding: 0;
        box-sizing: border-box;
        @include box-sizing(border-box);
      }
      .base-alert {
        display: flex;
        justify-content: center;
        align-items: center;
        position: fixed;
        @include box(100%, 100%);
        top: 0;
        left: 0;
        z-index: 2;
        .g-row-flex-right {
          display: flex;
          align-items: center;
          justify-content: flex-end;
        }
        .m-pop-close {
          position: absolute;
          right: 16px;
          top: 16px;
          cursor: pointer;
          z-index: 2;
        }
        .m-pop-title {
          font-size: 15px;
          font-weight: 500;
          color: #5A6371;
          text-indent: 24px;
          line-height: 40px;
          padding-top: 10px;
        }
        .m-pop-content {
          @include box(auto, auto);
          min-width: 372px;
          min-height: 156px;
          background: #fff;
          z-index: 1;
          box-shadow: 3px 5px 21px 0px rgba(183, 183, 183, 0.29);
          border: 1px solid #EBEBEB;
          padding-bottom: 24px;
          position: relative;
          border-radius: 8px;
        }
        .base-alert-bg {
          position: fixed;
          @include box(100%, 100%);
          top: 0;
          left: 0;
        }
        .m-pop-detail {
          padding-top: 32px;
        }
        .m-pop-message {
          text-align: center;
          font-size: 14px;
          font-weight: bold;
          color: #737478;
          margin-bottom: 5px;
        }
        .m-pop-submessage {
          font-size: 14px;
          font-weight: 400;
          color: #5A6371;
          text-align: left;
          padding-left: 40px;
          max-width: 372px;
          margin: auto;
        }
        .g-edit-list {
          margin-bottom: 16px;
        }
        .g-btn-group {
          margin-top: 30px;
          padding-right: 16px;
        }
        .g-message-info {
          font-size: 14px;
          font-weight: 400;
          text-align: center;
        }
        // 按钮样式
        .u-base-pop-create,
        .u-base-pop-cancel {
          @include box(90px, 32px);
          cursor: pointer;
          border: none;
          outline: none;
          display: flex;
          align-items: center;
          justify-content: center;
          color: #EFF7FF;
          font-size: 14px;
          font-weight: 500;
          border-radius: 4px;
        }
        .u-base-pop-create {
          background: #536FFF;
        }
        .u-base-pop-cancel {
          background-color: #fff;
          border: 1px solid;
          color: #2F51FF;
          border-color: #536FFF;
          margin-right: 20px;
          margin-bottom: 0;
        }
      }
    </style>
    
    

    2.2 vue2移除了vue.extend,我们使用createVNode和render来渲染弹窗
    然后挂载到 config.globalProperties 上
    index.js内容如下:

    import {createVNode, render} from 'vue'
    import HlAlert from './hl-alert.vue'
    
    const myAlert = function(options) {
        const container = document.createElement('div')
        const vm = createVNode(
          HlAlert,
          options
        )
        render(vm, container)
        document.body.appendChild(container)
    }  
    const MyHlAlert = {
      install(app) {
        // 配置此应用
        app.config.globalProperties.$hlAlert = myAlert
      }
    }
    export default MyHlAlert
    
    1. main.js中引入弹窗组件
    import { createApp } from 'vue'
    import App from './App.vue'
    import HlAlert from './components/hl-alert/index'
    
    import './assets/main.css'
    const app = createApp(App)
    app.use(HlAlert)
    app.mount('#app')
    
    1. 页面中使用
    <script setup>
      import {getCurrentInstance} from 'vue'
      const {appContext} = getCurrentInstance()
      const globalProxy = appContext.config.globalProperties  
      function alert () {
        globalProxy.$hlAlert({
          title: '审核未通过原因',
          subMessage: '审核未通审核未通审核未通审核未通审核未通',
          isCloseIcon: true,
          cancelBtnClass: 'm-red', // 取消按钮的class
          callback: () => {
            console.log('确定按钮')
          }
        })
      }
      function alert2 () {
        globalProxy.$hlAlert({
          title: '信息提示',
          subMessage: '只展示一个按钮',
          isCloseIcon: true,
          ensureBtnText: '确定', // 确认按钮的文字
          cancelBtnText: '取消', // 取消按钮的文字
          hideCancel: true // true 隐藏取消按钮
        })
      }
      function alert3 () {
        globalProxy.$hlAlert({
          title: '信息提示',
          subMessage: '确定按钮文字修改',
          isCloseIcon: true,
          ensureBtnText: '知道了', // 确认按钮的文字
          cancelBtnText: '取消', // 取消按钮的文字
          hideCancel: true // true 隐藏取消按钮
        })
      }
      function alert4 () {
        globalProxy.$hlAlert({
          title: '信息提示',
          subMessage: '<p style="text-align: left;color: red;">审核未通审核未通审核未通审核未通审核未通</p>',
          isCloseIcon: true,
          cancelBtnClass: 'm-red', // 取消按钮的class
          callback: () => {
            console.log('确定按钮')
          }
        })
      }
    </script>
    
    <template>
      <div>
        <button @click="alert">click1</button>
        <button @click="alert2">click2</button>
        <button @click="alert3">click3</button>
        <button @click="alert4">click4</button>
      </div>
    </template>
    
    <style lang="scss">
    .base-alert .m-red {
      border: 1px solid #536FFF;
      background: transparent;
      color: #536FFF;
      margin-right: 16px;
    }
    </style>
    
    1. 如果想把该组件发布到npm上,请参考文章 npm 发布自己写的vue3的组件

    相关文章

      网友评论

          本文标题:vue3 全局弹窗组件编写

          本文链接:https://www.haomeiwen.com/subject/xwpordtx.html