vue3+typescript 实现全局h5弹窗AlertDialog组件

废话少说先上图

基本使用


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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容