可自定义设置以下属性:
-
对话框宽度(width),类型:string | number,单位 px,默认 520
-
对话框高度(height),类型:string | number,单位 px,默认 'auto',自适应内容高度
-
标题(title),类型:string | slot,默认 undefined
-
自定义标题样式(titleStyle),类型:CSSProperties,默认 {}
-
内容(content),类型:string | slot,默认 undefined
-
自定义内容样式(contentStyle),类型:CSSProperties,默认 {}
-
自定义 body 类名(bodyClass),类型:string,默认 undefined
-
自定义 body 样式(bodyStyle),类型:CSSProperties,默认 {}
-
Scrollbar 组件属性配置(scrollbarProps),用于设置内容滚动条的样式,参考 Scrollbar Props,类型:object,默认 {}
-
取消按钮文字(cancelText),类型:string,默认 '取消'
-
取消按钮 props 配置(cancelProps),参考 Button 组件 Props,类型:object,默认 {}
-
确定按钮文字(okText),类型:string,默认 '确认'
-
确定按钮类型(okType),类型:'primary' | 'danger',默认 'primary'
-
确认按钮 props 配置(okProps),优先级高于 okType,参考 Button 组件 Props,类型:object,默认 {}
-
是否显示底部按钮(footer),类型:boolean | slot,默认 true
-
关闭时是否销毁 Dialog 里的子元素(destroyOnClose),类型:boolean,默认 false
-
是否允许切换全屏(switchFullscreen),允许后右上角会出现一个切换按钮,类型:boolean,默认 false
-
是否水平垂直居中(centered),否则固定高度水平居中,类型:boolean,默认 false
-
固定高度水平居中时,距顶部高度(top),仅当 centered: false 时生效,单位 px,类型:string | number,默认 100
-
对话框动画出现的位置(transformOrigin),类型:'mouse' | 'center',默认 'mouse'
-
确定按钮加载中(confirmLoading),类型:boolean,默认 false
-
是否在打开对话框时禁用背景滚动(blockScroll),类型:boolean,默认 true
-
是否支持键盘 esc 关闭(keyboard),类型:boolean,默认 true
-
点击蒙层是否允许关闭(maskClosable),类型:boolean,默认 true
-
自定义蒙层样式(maskStyle),类型:CSSProperties,默认 {}
-
对话框是否可见(v-model:open),类型:boolean,默认 false
效果如下图:
在线预览
①创建对话框组件Dialog.vue:
其中引入使用了以下组件与工具函数:
<script setup lang="ts">
import { ref, computed, watch, watchEffect, nextTick, onMounted, onUnmounted } from 'vue'
import type { CSSProperties } from 'vue'
import Scrollbar from 'components/scrollbar'
import Button from 'components/button'
import { useOptionsSupported } from 'components/utils'
export interface Props {
width?: string | number // 对话框宽度,单位 px
height?: string | number // 对话框高度,单位 px,默认自适应内容高度
title?: string // 标题 string | slot
titleStyle?: CSSProperties // 自定义标题样式
content?: string // 内容 string | slot
contentStyle?: CSSProperties // 自定义内容样式
bodyClass?: string // 自定义 body 类名
bodyStyle?: CSSProperties // 自定义 body 样式
scrollbarProps?: object // Scrollbar 组件属性配置,用于设置内容滚动条的样式
cancelText?: string // 取消按钮文字
cancelProps?: object // 取消按钮 props 配置,参考 Button 组件 Props
okText?: string // 确定按钮文字
okType?: 'primary' | 'danger' // 确定按钮类型
okProps?: object // 确认按钮 props 配置,优先级高于 okType,参考 Button 组件 Props
footer?: boolean // 是否显示底部按钮 boolean | slot
destroyOnClose?: boolean // 关闭时是否销毁 Dialog 里的子元素
switchFullscreen?: boolean // 是否允许切换全屏,允许后右上角会出现一个切换按钮
centered?: boolean // 是否水平垂直居中,否则固定高度水平居中
top?: string | number // 固定高度水平居中时,距顶部高度,仅当 centered: false 时生效,单位 px
transformOrigin?: 'mouse' | 'center' // 对话框动画出现的位置
confirmLoading?: boolean // 确定按钮 loading
blockScroll?: boolean // 是否在打开对话框时禁用背景滚动
keyboard?: boolean // 是否支持键盘 esc 关闭
maskClosable?: boolean // 点击蒙层是否允许关闭
maskStyle?: CSSProperties // 自定义蒙层样式
open?: boolean // 对话框是否可见
}
const props = withDefaults(defineProps<Props>(), {
width: 520,
height: 'auto',
title: undefined,
titleStyle: () => ({}),
content: undefined,
contentStyle: () => ({}),
bodyClass: undefined,
bodyStyle: () => ({}),
scrollbarProps: () => ({}),
cancelText: '取消',
cancelProps: () => ({}),
okText: '确定',
okType: 'primary',
okProps: () => ({}),
footer: true,
destroyOnClose: false,
switchFullscreen: false,
centered: false,
top: 100,
transformOrigin: 'mouse',
confirmLoading: false,
blockScroll: true,
keyboard: true,
maskClosable: true,
maskStyle: () => ({}),
open: false
})
const dialogRef = ref() // dialog DOM 引用
const mousePosition = ref<{ x: number; y: number } | null>(null) // 鼠标点击位置
const dialogOpen = ref<boolean>()
const showDialogWrap = ref<boolean>()
const transformOrigin = ref<string>('50% 50%')
const fullscreen = ref<boolean>(false)
const { isSupported: captureSupported } = useOptionsSupported('capture')
const emits = defineEmits(['update:open', 'cancel', 'ok'])
const dialogWidth = computed(() => {
if (typeof props.width === 'number') {
return `${props.width}px`
}
return props.width
})
const dialogHeight = computed(() => {
if (typeof props.height === 'number') {
return `${props.height}px`
}
return props.height
})
const dialogTop = computed(() => {
if (typeof props.top === 'number') {
return `${props.top}px`
}
return props.top
})
const dialogStyle = computed(() => {
if (fullscreen.value) {
if (props.transformOrigin === 'mouse') {
return {
width: '100%',
transformOrigin: `${mousePosition.value?.x}px ${mousePosition.value?.y}px`
} as CSSProperties
} else {
return {
width: '100%',
transformOrigin: transformOrigin.value
} as CSSProperties
}
} else {
if (props.centered) {
return {
width: dialogWidth.value,
transformOrigin: transformOrigin.value
} as CSSProperties
} else {
return {
width: dialogWidth.value,
transformOrigin: transformOrigin.value,
top: dialogTop.value
} as CSSProperties
}
}
})
const dialogBodyStyle = computed(() => {
if (fullscreen.value) {
return {
height: '100vh',
...props.bodyStyle
} as CSSProperties
} else {
return {
height: dialogHeight.value,
...props.bodyStyle
} as CSSProperties
}
})
watch(
dialogOpen,
async (to) => {
if (to) {
await nextTick()
dialogRef.value.focus()
if (props.blockScroll) {
// 锁定滚动
document.documentElement.style.overflowY = 'hidden'
document.body.style.overflowY = 'hidden'
}
} else {
if (props.blockScroll) {
// 解锁滚动
document.documentElement.style.removeProperty('overflow-y')
document.body.style.removeProperty('overflow-y')
}
}
},
{
immediate: true
}
)
watchEffect(() => {
dialogOpen.value = props.open
})
onMounted(() => {
document.addEventListener('click', getClickPosition, captureSupported.value ? { capture: true } : true) // 事件在捕获阶段执行
})
onUnmounted(() => {
document.removeEventListener('click', getClickPosition, captureSupported.value ? { capture: true } : true)
})
function getClickPosition(e: MouseEvent) {
if (!dialogOpen.value) {
mousePosition.value = {
x: e.clientX, // 相对于浏览器视口左上角的 X 坐标,不页面滚动而改变
y: e.clientY // 相对于浏览器视口左上角的 Y 坐标,不页面滚动而改变
}
}
}
async function onBeforeEnter(el: Element) {
showDialogWrap.value = true
await nextTick()
if (props.transformOrigin === 'mouse' && mousePosition.value) {
const rect = el.getBoundingClientRect()
transformOrigin.value = `${mousePosition.value.x - rect.left}px ${mousePosition.value.y - rect.top}px`
} else {
transformOrigin.value = '50% 50%'
}
}
function onBeforeLeave(el: Element) {
if (props.transformOrigin === 'mouse' && mousePosition.value) {
const rect = el.getBoundingClientRect()
transformOrigin.value = `${mousePosition.value.x - rect.left}px ${mousePosition.value.y - rect.top}px`
} else {
transformOrigin.value = '50% 50%'
}
}
function onAfterLeave() {
showDialogWrap.value = false
// 重置全屏显示
fullscreen.value = false
}
function onFullScreen() {
fullscreen.value = !fullscreen.value
}
function onCancel() {
dialogOpen.value = false
emits('update:open', false)
emits('cancel')
}
function onOk() {
emits('ok')
}
</script>
<template>
<div class="m-dialog-root">
<Transition name="fade">
<div v-show="dialogOpen" class="dialog-mask" :style="maskStyle"></div>
</Transition>
<div
v-show="showDialogWrap"
tabindex="-1"
ref="dialogRef"
class="m-dialog-wrap"
:class="{ 'flex-centered': centered }"
@click.self="props.maskClosable ? onCancel() : () => false"
@keydown.esc="props.keyboard ? onCancel() : () => false"
>
<Transition
name="zoom"
enter-from-class="zoom-enter"
enter-active-class="zoom-enter"
enter-to-class="zoom-enter zoom-enter-active"
leave-from-class="zoom-leave"
leave-active-class="zoom-leave zoom-leave-active"
leave-to-class="zoom-leave zoom-leave-active"
@before-enter="onBeforeEnter"
@before-leave="onBeforeLeave"
@after-leave="onAfterLeave"
>
<div
v-show="dialogOpen"
class="m-dialog"
:class="{ 'dialog-with-fullscreen': fullscreen }"
:style="dialogStyle"
>
<div v-if="!destroyOnClose" class="dialog-body-wrap" :class="bodyClass" :style="dialogBodyStyle">
<div class="dialog-header" :class="{ 'header-with-switch': switchFullscreen }" :style="titleStyle">
<slot name="title">{{ title }}</slot>
</div>
<span v-if="switchFullscreen" class="fullscreen-action" @click="onFullScreen">
<svg
v-show="!fullscreen"
focusable="false"
data-icon="fullscreen"
width="1em"
height="1em"
fill="currentColor"
aria-hidden="true"
viewBox="64 64 896 896"
>
<path
d="M290 236.4l43.9-43.9a8.01 8.01 0 00-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 0013.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 000 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 00-11.3 0l-42.4 42.3a8.03 8.03 0 000 11.3L734 787.6l-43.9 43.9a8.01 8.01 0 004.7 13.6L855 864c5.1.6 9.5-3.7 8.9-8.9L845 694.9zm-463.7-94.6a8.03 8.03 0 00-11.3 0L236.3 733.9l-43.7-43.7a8.01 8.01 0 00-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
v-show="fullscreen"
focusable="false"
data-icon="fullscreen-exit"
width="1em"
height="1em"
fill="currentColor"
aria-hidden="true"
viewBox="64 64 896 896"
>
<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 00-11.3 0l-42.4 42.3a8.03 8.03 0 000 11.3L280 333.6l-43.9 43.9a8.01 8.01 0 004.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 000 11.3l42.4 42.3c3.1 3.1 8.2 3.1 11.3 0L333.7 744l43.7 43.7A8.01 8.01 0 00391 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 00-11.3 0L690.3 279.9l-43.7-43.7a8.01 8.01 0 00-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 00-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>
</span>
<span class="close-action" @click="onCancel">
<svg
width="1em"
height="1em"
fill="currentColor"
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>
</span>
<Scrollbar v-bind="scrollbarProps">
<div class="dialog-content" :style="contentStyle">
<slot>{{ content }}</slot>
</div>
</Scrollbar>
<div v-if="footer" class="dialog-footer">
<slot name="footer">
<Button class="mr8" @click="onCancel" v-bind="cancelProps">{{ cancelText }}</Button>
<Button :type="okType" :loading="props.confirmLoading" @click="onOk" v-bind="okProps">{{
okText
}}</Button>
</slot>
</div>
</div>
<div v-if="destroyOnClose && dialogOpen" class="dialog-body-wrap" :class="bodyClass" :style="dialogBodyStyle">
<div class="dialog-header" :class="{ 'header-with-switch': switchFullscreen }" :style="titleStyle">
<slot name="title">{{ title }}</slot>
</div>
<span v-if="switchFullscreen" class="fullscreen-action" @click="onFullScreen">
<svg
v-show="!fullscreen"
focusable="false"
data-icon="fullscreen"
width="1em"
height="1em"
fill="currentColor"
aria-hidden="true"
viewBox="64 64 896 896"
>
<path
d="M290 236.4l43.9-43.9a8.01 8.01 0 00-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 0013.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 000 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 00-11.3 0l-42.4 42.3a8.03 8.03 0 000 11.3L734 787.6l-43.9 43.9a8.01 8.01 0 004.7 13.6L855 864c5.1.6 9.5-3.7 8.9-8.9L845 694.9zm-463.7-94.6a8.03 8.03 0 00-11.3 0L236.3 733.9l-43.7-43.7a8.01 8.01 0 00-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
v-show="fullscreen"
focusable="false"
data-icon="fullscreen-exit"
width="1em"
height="1em"
fill="currentColor"
aria-hidden="true"
viewBox="64 64 896 896"
>
<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 00-11.3 0l-42.4 42.3a8.03 8.03 0 000 11.3L280 333.6l-43.9 43.9a8.01 8.01 0 004.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 000 11.3l42.4 42.3c3.1 3.1 8.2 3.1 11.3 0L333.7 744l43.7 43.7A8.01 8.01 0 00391 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 00-11.3 0L690.3 279.9l-43.7-43.7a8.01 8.01 0 00-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 00-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>
</span>
<span class="close-action" @click="onCancel">
<svg
width="1em"
height="1em"
fill="currentColor"
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>
</span>
<Scrollbar v-bind="scrollbarProps">
<div class="dialog-content" :style="contentStyle">
<slot>{{ content }}</slot>
</div>
</Scrollbar>
<div v-if="footer" class="dialog-footer">
<slot name="footer">
<Button class="mr8" @click="onCancel" v-bind="cancelProps">{{ cancelText }}</Button>
<Button :type="okType" :loading="props.confirmLoading" @click="onOk" v-bind="okProps">{{
okText
}}</Button>
</slot>
</div>
</div>
</div>
</Transition>
</div>
</div>
</template>
<style lang="less" scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s linear;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.zoom-enter {
transform: none;
opacity: 0;
animation-duration: 0.3s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
animation-play-state: paused;
}
.zoom-enter-active {
animation-name: zoomIn;
animation-play-state: running;
@keyframes zoomIn {
0% {
transform: scale(0.2);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
}
.zoom-leave {
animation-duration: 0.2s;
animation-fill-mode: both;
animation-play-state: paused;
animation-timing-function: cubic-bezier(0.78, 0.14, 0.15, 0.86);
}
.zoom-leave-active {
animation-name: zoomOut;
animation-play-state: running;
@keyframes zoomOut {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(0.2);
opacity: 0;
}
}
}
.dialog-mask {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
z-index: 1000;
background: rgba(0, 0, 0, 0.45);
}
.m-dialog-wrap {
position: fixed;
inset: 0;
overflow: auto;
outline: 0;
z-index: 1010;
.m-dialog {
position: relative;
margin: 0 auto;
color: rgba(0, 0, 0, 0.88);
font-size: 14px;
line-height: 1.5714285714285714;
width: auto;
max-width: calc(100vw - 32px);
padding-bottom: 24px;
outline: none;
.dialog-body-wrap {
display: flex;
flex-direction: column;
position: relative;
background-color: #fff;
border-radius: 8px;
box-shadow:
0 6px 16px 0 rgba(0, 0, 0, 0.08),
0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 9px 28px 8px rgba(0, 0, 0, 0.05);
padding: 20px 24px;
.dialog-header {
font-size: 16px;
color: rgba(0, 0, 0, 0.88);
font-weight: 600;
line-height: 1.5;
word-break: break-all;
background: transparent;
border-radius: 8px 8px 0 0;
margin-bottom: 8px;
max-width: calc(100% - 24px);
}
.header-with-switch {
max-width: calc(100% - 54px);
}
.fullscreen-action {
.close-action();
right: 48px;
}
.close-action {
position: absolute;
top: 20px;
right: 18px;
z-index: 1010;
font-weight: 600;
line-height: 1;
background: transparent;
border-radius: 4px;
width: 22px;
height: 22px;
cursor: pointer;
transition: background 0.2s;
display: flex;
align-items: center;
justify-content: center;
svg {
font-size: 16px;
color: rgba(0, 0, 0, 0.45);
fill: currentColor;
transition: color 0.2s;
}
&:hover {
background: rgba(0, 0, 0, 0.06);
svg {
color: rgba(0, 0, 0, 0.88);
}
}
}
.dialog-content {
font-size: 14px;
color: rgba(0, 0, 0, 0.88);
line-height: 1.5714285714285714;
word-break: break-all;
transition: all 0.25s;
}
.dialog-footer {
text-align: end;
background: transparent;
margin-top: 12px;
.mr8 {
margin-right: 8px;
}
}
}
}
.dialog-with-fullscreen {
max-width: 100%;
padding-bottom: 0;
}
}
.flex-centered {
display: flex;
justify-content: center;
align-items: center;
.m-dialog {
padding-bottom: 0;
}
}
</style>
②在要使用的页面引入:
其中引入使用了以下组件:
<script setup lang="ts">
import Dialog from './Dialog.vue'
import { ref } from 'vue'
const open1 = ref(false)
const open2 = ref(false)
const open3 = ref(false)
const open4 = ref(false)
const open5 = ref(false)
const open6 = ref(false)
const open7 = ref(false)
const open8 = ref(false)
const open9 = ref(false)
const open10 = ref(false)
const open11 = ref(false)
const open12 = ref(false)
const open13 = ref(false)
const confirmLoading = ref(false)
function onCancel() {
// 点击蒙层或 Esc 键或右上角叉或取消按钮的回调
console.log('cancel')
}
function onOk() {
// 点击确定的回调
open1.value = false
open2.value = false
open3.value = false
open4.value = false
open5.value = false
open6.value = false
open7.value = false
open8.value = false
open9.value = false
open10.value = false
open11.value = false
open12.value = false
console.log('ok')
}
function handleCancel() {
// 点击蒙层或 Esc 键或右上角叉或取消按钮的回调
console.log('cancel')
}
function handleOk() {
// 点击确定回调
confirmLoading.value = true // 开启加载状态
setTimeout(() => {
confirmLoading.value = false
open13.value = false
console.log('ok')
}, 2000)
}
</script>
<template>
<div>
<h1>{{ $route.name }} {{ $route.meta.title }}</h1>
<h2 class="mt30 mb10">基本使用</h2>
<Button type="primary" @click="open1 = true">Open Dialog</Button>
<Dialog v-model:open="open1" title="Title" @cancel="onCancel" @ok="onOk">
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<h2 class="mt30 mb10">自定义宽高</h2>
<Button type="primary" @click="open2 = true">Open Dialog</Button>
<Dialog v-model:open="open2" :width="480" :height="200" @cancel="onCancel" @ok="onOk">
<template #title>Title</template>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Dialog>
<h2 class="mt30 mb10">自定义样式</h2>
<Space>
<Button type="primary" @click="open3 = true">Custom Body Class Dialog</Button>
<Button type="primary" @click="open4 = true">Custom Body & Mask Style Dialog</Button>
<Button type="primary" @click="open5 = true">Custom Title & Content Style Dialog</Button>
</Space>
<Dialog v-model:open="open3" title="Title" body-class="custom-class" @cancel="onCancel" @ok="onOk">
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<Dialog
v-model:open="open4"
title="Title"
:body-style="{
padding: '24px',
borderRadius: '16px'
}"
:mask-style="{
backgroundColor: 'rgba(0, 0, 0, 0.6)'
}"
@cancel="onCancel"
@ok="onOk"
>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<Dialog
v-model:open="open5"
title="Title"
:title-style="{
fontSize: '18px',
color: '#d4380d'
}"
:content-style="{
fontSize: '16px',
color: '#d4380d'
}"
@cancel="onCancel"
@ok="onOk"
>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<h2 class="mt30 mb10">自定义按钮</h2>
<Button type="primary" @click="open6 = true">Custom Btns Dialog</Button>
<Dialog
v-model:open="open6"
title="Title"
cancel-text="Return"
:cancel-props="{ type: 'danger', ghost: true }"
ok-text="Submit"
:ok-props="{ type: 'primary', ghost: true }"
@cancel="onCancel"
@ok="onOk"
>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<h2 class="mt30 mb10">隐藏底部按钮</h2>
<Button type="primary" @click="open7 = true">Open Dialog</Button>
<Dialog v-model:open="open7" title="Title" :footer="false" @cancel="onCancel" @ok="onOk">
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<h2 class="mt30 mb10">切换全屏</h2>
<Button type="primary" @click="open8 = true">Open Dialog</Button>
<Dialog v-model:open="open8" title="Title" switch-fullscreen @cancel="onCancel" @ok="onOk">
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<h2 class="mt30 mb10">自定义位置</h2>
<Space>
<Button type="primary" @click="open9 = true">Fixed Top Number Dialog</Button>
<Button type="primary" @click="open10 = true">Fixed Top Percent Dialog</Button>
<Button type="primary" @click="open11 = true">Vertically Centered Dialog</Button>
</Space>
<Dialog v-model:open="open9" title="60px Top Title" :top="60" @cancel="onCancel" @ok="onOk">
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<Dialog v-model:open="open10" title="20% Top Title" top="20%" @cancel="onCancel" @ok="onOk">
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<Dialog v-model:open="open11" title="Centered Title" centered @cancel="onCancel" @ok="onOk">
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<h2 class="mt30 mb10">动画出现位置</h2>
<Button type="primary" @click="open12 = true">Transform Origin Center Dialog</Button>
<Dialog v-model:open="open12" title="Title" transform-origin="center" @cancel="onCancel" @ok="onOk">
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
<h2 class="mt30 mb10">异步延迟关闭</h2>
<Button type="primary" @click="open13 = true">Delayed Close Dialog</Button>
<Dialog v-model:open="open13" title="Title" :confirm-loading="confirmLoading" @cancel="handleCancel" @ok="handleOk">
<p>Bla bla ...</p>
<p>Bla bla ...</p>
<p>Bla bla ...</p>
</Dialog>
</div>
</template>
<style lang="less" scoped>
:deep(.custom-class) {
.dialog-header {
color: #ff6900 !important;
}
.dialog-content {
color: #ff6900 !important;
}
}
</style>