Vue3对话框(Dialog)

news/2024/7/10 3:02:12 标签: typescript, less, vue

Vue2对话框(Dialog)

可自定义设置以下属性:

  • 标题(title),类型:string | slot,默认 '提示'

  • 内容(content),类型:string | slot,默认 ''

  • 宽度(width),类型:number,默认 640px

  • 高度(height),类型:number,默认 480px

  • 是否允许切换全屏,允许后右上角会出现一个按钮(switchFullscreen),类型:boolean,默认 false

  • 取消按钮文字(cancelText),类型:string,默认 '取消'

  • 确认按钮文字(okText),类型:string,默认 '确认'

  • 是否显示底部按钮(footer),类型:boolean,默认 false

  • 是否水平垂直居中(center),类型:boolean,默认 true,(false时是固定高度水平居中)

  • 固定高度水平居中时,距顶部高度(top),类型:number,默认 100px

  • 加载中(loading),类型:boolean,默认 false

  • 对话框是否可见(visible),类型:boolean,默认 false

效果如下图:(整体样式模仿ant-design-vue Modal,同时阴影覆盖浏览器窗口)

①创建对话框组件Dialog.vue

<script setup lang="ts">
import { ref, computed, watch } from 'vue'
const props = defineProps({
    title: { // 标题 string | slot
      type: String,
      default: '提示'
    },
    content: { // 内容 string | slot
      type: String,
      default: ''
    },
    width: { // 宽度,默认640
      type: Number,
      default: 640
    },
    height: { // 高度,默认480
      type: Number,
      default: 480
    },
    switchFullscreen: { // 是否允许切换全屏,允许后右上角会出现一个按钮
      type: Boolean,
      default: false
    },
    cancelText: { // 取消按钮文字
      type: String,
      default: '取消'
    },
    okText: { // 确认按钮文字
      type: String,
      default: '确定'
    },
    footer: { // 是否显示底部按钮,默认不显示
      type: Boolean,
      default: false
    },
    center: { // 水平垂直居中:true  固定高度水平居中:false
      type: Boolean,
      default: true
    },
    top: { // 固定高度水平居中时,距顶部高度
      type: Number,
      default: 100
    },
    loading: { // 加载中
      type: Boolean,
      default: false
    },
    visible: { // 对话框是否可见
      type: Boolean,
      default: false
    }
  })

const fullScreen = ref(false)
const dialogWidth = computed(() => {
  if (fullScreen.value) {
      return '100%'
    } else {
      return props.width + 'px'
    }
  })
const dialogHeight = computed(() => {
    if (fullScreen.value) {
      return '100vh'
    } else {
      return props.height + 'px'
    }
  })
watch(
  () => props.visible, (to) => {
    if (!to) { // 重置全屏显示
      fullScreen.value = false
    }
})
const emits = defineEmits(['close', 'cancel', 'ok'])
function onBlur () {
  if (!props.loading) {
    emits('close')
  }
}
function onFullScreen () {
  fullScreen.value = !fullScreen.value
}
function onClose () {
  emits('close')
}
function onCancel () {
  emits('cancel')
}
function onConfirm () {
  emits('ok')
}
</script>
<template>
  <Transition>
    <div class="m-dialog-mask" v-show="visible" @click.self="onBlur">
      <div :class="['m-dialog', center ? 'relative-hv-center' : 'top-center']" :style="`width: ${dialogWidth}; height: ${dialogHeight}; top: ${!center ? top + 'px':'50%'}`">
        <div class="m-dialog-content" :class="{loading: loading}">
          <div class="m-spin-dot" v-show="loading">
            <span class="u-dot-item"></span>
            <span class="u-dot-item"></span>
            <span class="u-dot-item"></span>
            <span class="u-dot-item"></span>
          </div>
          <svg @click="onFullScreen" v-show="!fullScreen&&switchFullscreen" class="u-screen" viewBox="64 64 896 896" data-icon="fullscreen" aria-hidden="true" focusable="false"><path d="M290 236.4l43.9-43.9a8.01 8.01 0 0 0-4.7-13.6L169 160c-5.1-.6-9.5 3.7-8.9 8.9L179 329.1c.8 6.6 8.9 9.4 13.6 4.7l43.7-43.7L370 423.7c3.1 3.1 8.2 3.1 11.3 0l42.4-42.3c3.1-3.1 3.1-8.2 0-11.3L290 236.4zm352.7 187.3c3.1 3.1 8.2 3.1 11.3 0l133.7-133.6 43.7 43.7a8.01 8.01 0 0 0 13.6-4.7L863.9 169c.6-5.1-3.7-9.5-8.9-8.9L694.8 179c-6.6.8-9.4 8.9-4.7 13.6l43.9 43.9L600.3 370a8.03 8.03 0 0 0 0 11.3l42.4 42.4zM845 694.9c-.8-6.6-8.9-9.4-13.6-4.7l-43.7 43.7L654 600.3a8.03 8.03 0 0 0-11.3 0l-42.4 42.3a8.03 8.03 0 0 0 0 11.3L734 787.6l-43.9 43.9a8.01 8.01 0 0 0 4.7 13.6L855 864c5.1.6 9.5-3.7 8.9-8.9L845 694.9zm-463.7-94.6a8.03 8.03 0 0 0-11.3 0L236.3 733.9l-43.7-43.7a8.01 8.01 0 0 0-13.6 4.7L160.1 855c-.6 5.1 3.7 9.5 8.9 8.9L329.2 845c6.6-.8 9.4-8.9 4.7-13.6L290 787.6 423.7 654c3.1-3.1 3.1-8.2 0-11.3l-42.4-42.4z"></path></svg>
          <svg @click="onFullScreen" v-show="fullScreen&&switchFullscreen" class="u-screen" viewBox="64 64 896 896" data-icon="fullscreen-exit" aria-hidden="true" focusable="false"><path d="M391 240.9c-.8-6.6-8.9-9.4-13.6-4.7l-43.7 43.7L200 146.3a8.03 8.03 0 0 0-11.3 0l-42.4 42.3a8.03 8.03 0 0 0 0 11.3L280 333.6l-43.9 43.9a8.01 8.01 0 0 0 4.7 13.6L401 410c5.1.6 9.5-3.7 8.9-8.9L391 240.9zm10.1 373.2L240.8 633c-6.6.8-9.4 8.9-4.7 13.6l43.9 43.9L146.3 824a8.03 8.03 0 0 0 0 11.3l42.4 42.3c3.1 3.1 8.2 3.1 11.3 0L333.7 744l43.7 43.7A8.01 8.01 0 0 0 391 783l18.9-160.1c.6-5.1-3.7-9.4-8.8-8.8zm221.8-204.2L783.2 391c6.6-.8 9.4-8.9 4.7-13.6L744 333.6 877.7 200c3.1-3.1 3.1-8.2 0-11.3l-42.4-42.3a8.03 8.03 0 0 0-11.3 0L690.3 279.9l-43.7-43.7a8.01 8.01 0 0 0-13.6 4.7L614.1 401c-.6 5.2 3.7 9.5 8.8 8.9zM744 690.4l43.9-43.9a8.01 8.01 0 0 0-4.7-13.6L623 614c-5.1-.6-9.5 3.7-8.9 8.9L633 783.1c.8 6.6 8.9 9.4 13.6 4.7l43.7-43.7L824 877.7c3.1 3.1 8.2 3.1 11.3 0l42.4-42.3c3.1-3.1 3.1-8.2 0-11.3L744 690.4z"></path></svg>
          <svg @click="onClose" class="u-close" viewBox="64 64 896 896" data-icon="close" aria-hidden="true" focusable="false"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg>
          <div class="m-dialog-header">
            <slot name="title">
              <div class="u-head">{{ title }}</div>
            </slot>
          </div>
          <div class="m-dialog-body" :style="`height: calc(${dialogHeight} - ${footer ? '158px':'103px'});`">
            <slot>{{ content }}</slot>
          </div>
          <div class="m-dialog-footer" v-show="footer">
            <button class="u-cancel" @click="onCancel">{{ cancelText }}</button>
            <button class="u-confirm" @click="onConfirm">{{ okText }}</button>
          </div>
        </div>
      </div>
    </div>
  </Transition>
</template>
<style lang="less" scoped>
.v-enter-active, .v-leave-active {
  transition: opacity 0.3s ease;
}
.v-enter-from, .v-leave-to {
  opacity: 0;
}
.flex-hv-center { // 水平垂直居中方法①:弹性布局,随内容增大高度,并自适应水平垂直居中
  display: flex;
  justify-content: center;
  align-items: center;
}
.relative-hv-center { // 水平垂直居中方法②:相对定位,随内容增大高度,并自适应水平垂直居中
  position: relative;
  top: 50%;
  transform: translateY(-50%);
  -ms-transform: translateY(-50%);; /* IE 9 */
  -webkit-transform: translateY(-50%); /* Safari and Chrome */
}
.top-center { // 相对定位,固定高度,始终距离视图顶端100px
  position: relative;
  // top: 100px;
}
.m-dialog-mask {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 10000;
  background: rgba(0,0,0,0.45);
  .m-dialog {
    margin: 0 auto;
    transition: all .3s ease;
    .m-spin-dot { // 绝对定位,并设置水平垂直居中
      position: absolute;
      display: inline-block;
      right: 0;
      left: 0;
      top: 0;
      bottom: 0;
      margin: auto;
      width: 24px;
      height: 24px;
      transform: rotate(45deg);
      -ms-transform: rotate(45deg); /* Internet Explorer */
      -moz-transform: rotate(45deg); /* Firefox */
      -webkit-transform: rotate(45deg); /* Safari 和 Chrome */
      -o-transform: rotate(45deg); /* Opera */
      animation: rotate 1.2s linear infinite;
      -webkit-animation: rotate 1.2s linear infinite;
      @keyframes rotate {
        100% {transform: rotate(405deg);}
      }
      .u-dot-item { // 单个圆点样式
        position: absolute;
        width: 8px;
        height: 8px;
        background: @themeColor;
        border-radius: 50%;
        opacity: .3;
        animation: spinMove 1s linear infinite alternate;
        -webkit-animation: spinMove 1s linear infinite alternate;
        @keyframes spinMove {
          100% {opacity: 1;}
        }
      }
      .u-dot-item:first-child {
        top: 0;
        left: 0;
      }
      .u-dot-item:nth-child(2) {
        top: 0;
        right: 0;
        animation-delay: .4s;
        -webkit-animation-delay: .4s;
      }
      .u-dot-item:nth-child(3) {
        bottom: 0;
        right: 0;
        animation-delay: .8s;
        -webkit-animation-delay: .8s;
      }
      .u-dot-item:last-child {
        bottom: 0;
        left: 0;
        animation-delay: 1.2s;
        -webkit-animation-delay: 1.2s;
      }
    }
    .loading { // 加载过程背景虚化
      background: rgb(248, 248, 248) !important;
      pointer-events: none; // 屏蔽鼠标事件
    }
    .m-dialog-content {
      position: relative;
      background: #fff;
      border-radius: 4px;
      box-shadow: 0 4px 12px rgba(0,0,0,.1);
      .u-screen {
        .u-close();
        right: 64px;
      }
      .u-close {
        width: 16px;
        height: 16px;
        position: absolute;
        top: 19px;
        right: 24px;
        fill: rgba(0,0,0,.45);
        cursor: pointer;
        transition: fill .3s;
        &:hover {
          fill: rgba(0,0,0,.75);
        }
      }
      .m-dialog-header {
        height: 22px;
        padding: 16px 24px;
        color: rgba(0,0,0,.65);
        border-radius: 4px 4px 0 0;
        border-bottom: 1px solid #e8e8e8;
        .u-head {
          margin: 0;
          color: rgba(0,0,0,.85);
          font-weight: 500;
          font-size: 16px;
          line-height: 22px;
          word-wrap: break-word;
        }
      }
      .m-dialog-body {
        padding: 24px;
        font-size: 16px;
        line-height: 1.5;
        word-wrap: break-word;
        overflow: auto;
        transition: all .3s;
      }
      .m-dialog-footer {
        padding: 10px 16px;
        text-align: right;
        border-top: 1px solid #e8e8e8;
        .u-cancel {
          height: 32px;
          line-height: 32px;
          padding: 0 15px;
          font-size: 16px;
          border-radius: 4px;
          color: rgba(0,0,0,.65);
          background: #fff;
          border: 1px solid #d9d9d9;
          cursor: pointer;
          transition: all .3s cubic-bezier(.645,.045,.355,1);
          &:hover {
            color: fade(@themeColor, 80%);
            border-color: fade(@themeColor, 80%);
          }
          &:focus {
            color: shade(@themeColor, 12%);
            border-color: shade(@themeColor, 12%);
          }
        }
        .u-confirm {
          margin-left: 8px;
          height: 32px;
          line-height: 32px;
          padding: 0 15px;
          font-size: 16px;
          border-radius: 4px;
          background: @themeColor;
          border: 1px solid @themeColor;
          color: #fff;
          transition: all .3s cubic-bezier(.645,.045,.355,1);
          cursor: pointer;
          &:hover {
            background: fade(@themeColor, 80%);
            border-color: fade(@themeColor, 80%);
          }
          &:focus {
            background: shade(@themeColor, 12%);
            border-color: shade(@themeColor, 12%);
          }
        }
      }
    }
  }
}
</style>

②在要使用的页面引入:

<script setup lang="ts">
import { Dialog } from './Dialog.vue'
import { ref } from 'vue'
import { rafTimeout } from '../../packages'

const center = ref(true)
const footer = ref(false)
const loading = ref(false)
const visible = ref(false)
const title = ref('Dialog Title')
const content = ref('Content of the modal ...')
function showDialog (info: string) {
  footer.value = false
  center.value = true
  content.value = info
  visible.value = true
}
function showFooterDialog (info: string) {
  footer.value = true
  center.value = true
  content.value = info
  visible.value = true
}
function showCenterDialog (info: string) {
  center.value = true
  content.value = info
  visible.value = true
}
function showFixDialog (info: string) {
  center.value = false
  content.value = info
  visible.value = true
}
function onClose () { // 关闭回调
  visible.value = false
}
function onCancel () { // “取消”按钮回调
  visible.value = false
}
function onConfirm () { // “确定”,“知道了”按钮回调
  loading.value = true // 开启加载状态
  rafTimeout(() => {
    visible.value = false
    loading.value = false
  }, 500)
}
</script>
<template>
  <div>
    <h2 class="mb10">Dialog 对话框基本使用</h2>
    <Button class="mr30" @click="showDialog('Some descriptions ...')">默认对话框</Button>
    <Button class="mr30" @click="showFooterDialog('Some descriptions ...')">有底部按钮的对话框</Button>
    <Button class="mr30" @click="showCenterDialog('Some descriptions ...')">水平垂直居中对话框</Button>
    <Button class="mr30" @click="showFixDialog('Some descriptions ...')">高度固定对话框</Button>
    <Dialog
      :title="title"
      :width="720"
      :height="480"
      :content="content"
      :footer="footer"
      cancelText="取消"
      okText="确认"
      switchFullscreen
      @close="onClose"
      @cancel="onCancel"
      @ok="onConfirm"
      :center="center"
      :top="100"
      :loading="loading"
      :visible="visible">
      <template #title>
        <p class="u-title">Title</p>
      </template>
      <p>Bla bla ...</p>
      <p>Bla bla ...</p>
      <p>Bla bla ...</p>
    </Dialog>
  </div>
</template>
<style lang="less" scoped>
.u-title {
  font-size: 16px;
}
</style>

http://www.niftyadmin.cn/n/206673.html

相关文章

c语言学习之调试技巧

调试的学习这里以VS为例 目录 1.调试的基本步骤 2.Debug和Release的介绍 3.windows的调试环境 4.调试的时候查看程序当前的信息 1.查看临时变量 1.自动窗口 2.局部变量窗口 3.监视窗口 4.内存窗口 5.反汇编窗口 6.寄存器窗口 7.调用堆栈窗口 1.调试的基本步骤 .发现…

QVariant 类的应用

1、概述 QVariant 是Qt中的一个类&#xff0c;它可以存储任何数据类型的值&#xff0c;并提供了一些方便的方法来转换这些值&#xff0c;同时也可以在Qt中使用信号和槽机制传递任何类型的数据。 QVariant可以存储各种基本数据类型&#xff0c;如整数、浮点数、字符串、布尔值…

JSP开发模型

一、学习目标掌握MVC设计模式的原理、JSP Model1和JSP Model2模型的原理二、JSP开发模型--JSP Model在早期使用JSP开发的Java Web应用中&#xff0c;JSP文件是一个独立的、能自主完成所有任务的模块&#xff0c;它负责处理业务逻辑、控制网页流程和向用户展示页面等。从图中可以…

没有其他电子商务平台可以做WooCommerce所做的事情

电子商务驱动型企业的成功实际上取决于您的商店的有效性。因此&#xff0c;当某些事情不起作用时&#xff0c;您必须快速对其进行修复。 No Pong 的商店一团糟——尽管我们努力把事情做好。 根据自定义模板开发高性能品牌电子商务网站 全球超500万个电商网站采用&#xff0…

Scratch游戏“刀光剑影”游戏介绍

这是一个 Scratch 打怪 闯关 2d 的游戏。 资源下载网址&#xff1a; https://download.csdn.net/download/leyang0910/87651781 士兵简介&#xff1a; 剑兵 &#xff1a;4滴血&#xff0c;可以挥剑杀敌盾兵 &#xff1a;8滴血&#xff0c;盾牌可以防御或者怼人飞刀兵 &…

为什么Elasticsearch7.x把type给干掉了?

一、介绍 ES7之前是有type的&#xff0c;属于index下&#xff0c;一个index可以有不同的type&#xff0c;ES7开始就把type这个显示概念给删除了&#xff0c;统一换成了_doc来表示type。也就是ES7开始一个index只能有一个type&#xff0c;而且这个type还是默认的_doc。 二、type…

1_Linux存储的基本管理

1_Linux存储的基本管理1、设备识别2、设备挂载3、设备中文件的查找4、分区5、swap分区6、磁盘配额测试习题1、设备识别 设备接入系统后都是以文件的形式存在 设备文件名称&#xff1a; SATA/SAS/USB /dev/sda,/dev/sdb ##s SATA, dDISK a第几快 IDE /dev/hd0,/dev/hd1 #…

普乐蛙元宇宙VR体验馆项目|VR太空互动体验馆设备|虚拟现实VR体验馆

元宇宙VR&#xff0c;想必现在很多人都知道&#xff0c;有这款设备&#xff0c;可能未来的一个趋势就是元宇宙这样的一个环境&#xff0c;大家可能听过元宇宙VR&#xff0c;但可能具体的还是不清楚。元宇宙(Metaverse)&#xff0c;是人类运用数字技术构建的&#xff0c;由现实世…