美文网首页
vue3+typescript 实现全局h5弹窗AlertDia

vue3+typescript 实现全局h5弹窗AlertDia

作者: Pino | 来源:发表于2023-01-31 16:49 被阅读0次

    废话少说先上图

    基本使用


    image.png

    可以使用输入框 editable设置为true即可


    image.png
    直接拷贝AlertDialog.ts和alert-dialog.vue,稍微更改一下导入路径及修改css少量样式就可以使用了
    使用方式也非常简单 ,一看就会

    坐标不传的话 默认居中 ,传了坐标 组件有算法 设置弹窗跟随鼠标位置,不需要自己计算弹窗位置

    AlertDialog.show({
          title: '提示',
          content: '确定要作废该条订单吗?',
          left: event.x, 
          top: event.y,
          editable: false,
          canceledOnTouchOutside: true,
          result: async (res: ResultParam) => {
            if (res.confirm) {
              console.log('web', '用户确定')
            } else if (res.cancle) {
              console.log('web', '用户取消')
            }
          }
        })
     // 如果不喜欢回调的方式 可以使用await ,即showByAwait方法
     // await用法
      let res = await AlertDialog.showByAwait({
          title: '提示',
          content: '确定要作废该条订单吗?',
      })
       if (res.confirm) {
       } else if (res.cancle) {
           console.log('web', '用户取消')
      }
    

    vue3中已经不再支持用vue.extend来创建组件,那全局弹窗的代替方案是啥呢?这里推荐createApp来创建
    下面是完整代码 唯一不同就是你需要更换一下路径,下面看关键类(AlertDialog.ts) createApp方法主要在show里面调用

    import {createApp} from 'vue'
    import AlertDialogCom from '@assets/components/dialog/alert-dialog.vue'
    
    // prettier-ignore
    interface DialogParam {
        title?: string
        content?: string
        left?: number  // 弹窗距离左侧的距离 不传则默认居中
        top?: number   // 弹窗距离顶侧的距离 不传则默认居中
        cancelText?: string // 传入空字符串则不会显示取消按钮
        sureText?: string   // 传入空字符串则不会显示确认按钮
        editable?: boolean  // 是否显示输入框
        canceledOnTouchOutside?: boolean // 点击空白处关闭弹窗
        result?: Function   // 回调用户操作结果
    }
    
    // prettier-ignore
    export interface ResultParam {
        content: string  // editable 为 true 时,用户输入的文本
        confirm: boolean // 为 true 时,表示用户点击了确定按钮
        cancle: boolean  // 为 true 时,表示用户点击了取消
    }
    
    export default class AlertDialog{
      private static mountNode: HTMLElement | null = null
    
      public static show(param: DialogParam) {
        let app = createApp(AlertDialogCom, {...param})
        this.mountNode = document.createElement('div')
        app.mount(this.mountNode)
        document.body.appendChild(this.mountNode)
      }
    
      // await用法
      // let res = await DialogAialog.showByAwait({
      //     title: '提示',
      //     content: '确定要作废该条订单吗?',
      // })
      // if (res.confirm) {
      //
      // } else if (res.cancle) {
      //     console.log('web', '用户取消')
      // }
      public static showByAwait(param: DialogParam): Promise<ResultParam> {
        return new Promise((resolve, reject) => {
          param.result = (res: ResultParam) => resolve(res)
          this.show(param)
        })
      }
    
      public static close() {
        if (!this.mountNode) return
        document.body.removeChild(this.mountNode)
        this.mountNode = null
        // console.log('close', 'DialogAialog已销毁')
      }
    }
    

    弹窗组件(alert-dialog.vue)
    1.关闭按钮是使用阿里巴巴图标库 可以自己使用图片代替
    2.css样式很多都是引用var引用项目配置,可自行修改

    <template>
      <div class="dialog w-h-100 flex-row flex-center" @click="touchOutside">
        <div ref="refContent" class="dialog-content flex-column pd-10-15 relative" :style="style">
          <div class="dialog-title flex-row flex-between">
            <span>{{ title }}</span>
            <i class="dialog-icon-close t1font t1-close pointer" @click="close"></i>
          </div>
          <div class="content text-secondary">{{ content }}</div>
          <div v-if="editable" class="t-input">
            <input ref="tInput" v-model="inputContent" type="text" placeholder="" class="t-input__inner" />
          </div>
          <div class="footer w-100 flex-row flex-end">
            <button v-if="sureText.length > 0" @click="result(true)" class="btn-confirm">{{ sureText }}</button>
            <button v-if="cancelText.length > 0" @click="result(false)" class="btn-cancel">{{ cancelText }}</button>
          </div>
        </div>
      </div>
    </template>
    
    <script lang="ts" setup>
      /**
       * @用途
       * @author Pino
       * @创建时间 2023-01-11 15:19
       **/
      import DialogAialog from '@assets/components/dialog/AlertDialog'
      import {computed, onMounted, ref, StyleValue} from 'vue'
    
      const props = defineProps({
        title: {type: String, default: '提示'},
        content: {type: String, default: '确定要删除该数据吗?'},
        left: {type: Number, default: 0},
        top: {type: Number, default: 0},
        cancelText: {type: String, default: '取消'},
        sureText: {type: String, default: '确定'},
        editable: {type: Boolean, default: false},
        canceledOnTouchOutside: {type: Boolean, default: true},
        result: {type: Function}
      })
    
      let refContent = ref<HTMLElement | null>(null)
      let style = computed(() => {
        let style: Partial<StyleValue> = {}
        if (props.left || props.top) {
          style.position = 'absolute'
          let cw = document.documentElement.clientWidth
          let ch = document.documentElement.clientHeight
          // 弹窗的宽高
          let contentH = refContent.value?.offsetHeight || 0
          let contentW = refContent.value?.offsetWidth || 0
          let offset = 30 // 调整间隔
          if (props.left < (cw * 1) / 3) {
            // 鼠标点击在屏幕左侧,弹窗要显示在右侧
            style.left = props.left + offset + 'px'
            let top = props.top
            if (ch - top < contentH) top = ch - contentH // 如果鼠标距离最底部 的距离小于弹窗高度
            style.top = top + 'px'
          } else if (props.left > (cw * 1) / 3 && props.left < (cw * 2) / 3) {
            if (props.top < ch / 2) {
              // 鼠标在中间上方点击 则弹窗在正下方显示
              style.top = props.top + offset + 'px'
            } else {
              // 鼠标在中间下方点击 则弹窗在正上方显示
              style.top = props.top - contentH - offset + 'px'
            }
            style.left = props.left - contentW / 2 + 'px'
          } else {
            // 点击屏幕右侧
            style.left = props.left - contentW - offset + 'px'
            let top = props.top
            if (ch - top < contentH) top = ch - contentH // 如果鼠标距离最底部 的距离小于弹窗高度
            style.top = top + 'px'
          }
        }
        return style
      })
    
      let inputContent = ref('')
      let tInput = ref<HTMLElement | null>(null)
      onMounted(() => {
        if (props.editable) setTimeout(() => tInput.value?.focus(), 100)
      })
    
      let touchOutside = (e: any) => {
        if (!props.canceledOnTouchOutside) return
        if (e.target?.classList?.length && e.target.classList[0] == 'dialog') close()
      }
      const close = () => DialogAialog.close()
    
      let result = (isSure: boolean) => {
        if (props.result) props.result({content: inputContent.value, confirm: isSure == true, cancle: isSure == false})
        close()
      }
    </script>
    
    <style scoped>
      @import '@assets/ff-ui/icon-font/t1-icon.css';
    
      .dialog {
        position: fixed;
        top: 0;
        right: 0;
        left: 0;
        bottom: 0;
        z-index: 1024;
        background-color: rgba(0, 0, 0, 0.5);
      }
    
      .dialog-content {
        border-radius: 5px;
        box-shadow: 0 1px 3px rgb(0 0 0 / 30%);
        width: 400px;
        height: max-content;
        background-color: var(--bg-2);
        box-sizing: border-box;
      }
    
      .dialog-title {
        color: white;
        font-size: 16px;
      }
    
      .content {
        font-size: 14px;
      }
    
      .btn-confirm,
      .btn-cancel {
        width: 60px;
        height: 30px;
        border: none;
        outline: none;
        color: #fff;
        background-color: #1664ff;
        margin-left: 10px;
        border-radius: 3px;
        cursor: pointer;
      }
    
      .btn-cancel {
        color: #409eff;
        border: 1px solid #1664ff;
        background-color: #fff;
      }
    
      .btn-cancel:hover {
        background: #ecf5ff;
        color: var(--txt-secondary);
      }
    
      .btn-confirm:hover {
        opacity: 0.8;
      }
    
      .dialog-content > div {
        margin-top: 10px;
      }
    
      .t-input {
        font-size: 14px;
        display: inline-block;
        width: 100%;
      }
    
      .t-input__inner {
        -webkit-appearance: none;
        background-color: #fff;
        background-image: none;
        border-radius: 4px;
        border: 1px solid #dcdfe6;
        box-sizing: border-box;
        color: #606266;
        display: inline-block;
        font-size: inherit;
        height: 40px;
        line-height: 40px;
        outline: none;
        padding: 0 15px;
        transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
        width: 100%;
      }
    
      .dialog-icon-close {
        color: #909399;
        line-height: 15px;
        font-size: 15px;
      }
    
      .dialog-icon-close:hover {
        color: var(--color-link);
      }
    </style>
    

    相关文章

      网友评论

          本文标题:vue3+typescript 实现全局h5弹窗AlertDia

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