useDialog
useDialog 基于 ElDialog 提供了更清晰的状态控制方式。你可以在保留原始 Props、Slots 与事件能力的前提下,通过控制器方法完成打开、关闭与动态配置更新。
与手动维护 v-model 相比,useDialog 将可见状态统一收敛在 Hook 内部,适合在复杂交互场景中保持视图与逻辑解耦。
基础用法
vue
<script setup lang="ts">
import { useDialog, useMessageBox } from 'element-hooks';
const messageBox = useMessageBox();
const [Dialog, { open, close }] = useDialog({
width: 500,
title: 'Tips',
async beforeClose(done) {
const isConfirmed = await messageBox.confirm(
'Are you sure to close this dialog?',
);
if (isConfirmed) {
done();
}
},
});
</script>
<template>
<el-button plain @click="open">Click to open the Dialog</el-button>
<Dialog>
<span>This is a message</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">Cancel</el-button>
<el-button type="primary" @click="close">Confirm</el-button>
</div>
</template>
</Dialog>
</template>自定义内容
vue
<script setup lang="ts">
import { useDialog, useTable, useForm } from 'element-hooks';
import { ElInput, ElSelect } from 'element-plus';
const gridData = [
{
date: '2016-05-02',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
{
date: '2016-05-04',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
{
date: '2016-05-01',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
{
date: '2016-05-03',
name: 'John Smith',
address: 'No.1518, Jinshajiang Road, Putuo District',
},
];
const formLabelWidth = '140px';
const [Table] = useTable({
data: gridData,
columns: [
{ prop: 'date', label: 'Date', width: 150 },
{ prop: 'name', label: 'Name', width: 200 },
{ prop: 'address', label: 'Address' },
],
});
const [Form] = useForm({
model: {
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: '',
},
items: [
{
label: 'Promotion name',
labelWidth: formLabelWidth,
prop: 'name',
render: {
component: ElInput,
props: { autocomplete: 'off' },
},
},
{
label: 'Zones',
labelWidth: formLabelWidth,
prop: 'region',
render: {
component: ElSelect,
props: {
placeholder: 'Please select a zone',
options: [
{ label: 'Zone No.1', value: 'shanghai' },
{ label: 'Zone No.2', value: 'beijing' },
],
},
},
},
],
});
const [TableDialog, { open: openTableDialog }] = useDialog({
title: 'Shipping address',
width: 800,
});
const [FormDialog, { open: openFormDialog, close: closeFormDialog }] =
useDialog({
title: 'Shipping address',
width: 500,
});
</script>
<template>
<div class="flex flex-wrap gap-1">
<el-button class="!ml-0" plain @click="openTableDialog">
Open a Table nested Dialog
</el-button>
<el-button class="!ml-0" plain @click="openFormDialog">
Open a Form nested Dialog
</el-button>
</div>
<TableDialog>
<Table />
</TableDialog>
<FormDialog>
<Form />
<template #footer>
<div class="dialog-footer">
<el-button @click="closeFormDialog">Cancel</el-button>
<el-button type="primary" @click="closeFormDialog"> Confirm </el-button>
</div>
</template>
</FormDialog>
</template>自定义头部
vue
<script setup lang="ts">
import { useDialog } from 'element-hooks';
import { CircleCloseFilled } from '@element-plus/icons-vue';
const [Dialog, { open }] = useDialog({
showClose: false,
width: 500,
});
</script>
<template>
<el-button plain @click="open">
Open Dialog with customized header
</el-button>
<Dialog>
<template #header="{ close, titleId, titleClass }">
<div class="my-header">
<h4 :id="titleId" :class="titleClass">This is a custom header!</h4>
<el-button type="danger" @click="close">
<el-icon class="el-icon--left"><CircleCloseFilled /></el-icon>
Close
</el-button>
</div>
</template>
This is dialog content.
</Dialog>
</template>
<style scoped>
.my-header {
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 16px;
}
</style>嵌套的对话框
vue
<script setup lang="ts">
import { useDialog } from 'element-hooks';
const [OuterDialog, { open: openOuter, close: closeOuter }] = useDialog({
title: 'Outer Dialog',
width: 800,
});
const [InnerDialog, { open: openInner }] = useDialog({
title: 'Inner Dialog',
width: 500,
appendToBody: true,
});
</script>
<template>
<el-button plain @click="openOuter">Open the outer Dialog</el-button>
<OuterDialog>
<span>This is the outer Dialog</span>
<InnerDialog>
<span>This is the inner Dialog</span>
</InnerDialog>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeOuter">Cancel</el-button>
<el-button type="primary" @click="openInner">
Open the inner Dialog
</el-button>
</div>
</template>
</OuterDialog>
</template>内容居中
vue
<script setup lang="ts">
import { useDialog } from 'element-hooks';
const [Dialog, { open, close }] = useDialog({
title: 'Warning',
width: 500,
center: true,
});
</script>
<template>
<el-button plain @click="open">Click to open the Dialog</el-button>
<Dialog>
<span>
It should be noted that the content will not be aligned in center by
default
</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">Cancel</el-button>
<el-button type="primary" @click="close">Confirm</el-button>
</div>
</template>
</Dialog>
</template>居中对话框
vue
<script setup lang="ts">
import { useDialog } from 'element-hooks';
const [Dialog, { open, close }] = useDialog({
title: 'Warning',
width: 500,
alignCenter: true,
});
</script>
<template>
<el-button plain @click="open">Click to open the Dialog</el-button>
<Dialog>
<span>Open the dialog from the center from the screen</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">Cancel</el-button>
<el-button type="primary" @click="close">Confirm</el-button>
</div>
</template>
</Dialog>
</template>关闭时销毁
vue
<script setup lang="ts">
import { useDialog } from 'element-hooks';
const [Dialog, { open, close }] = useDialog({
title: 'Notice',
width: 500,
destroyOnClose: true,
center: true,
});
</script>
<template>
<el-button plain @click="open">Click to open Dialog</el-button>
<Dialog>
<span>
Notice: before the dialog is opened for the first time, this node and the
one below will not be rendered.
</span>
<div>
<strong>Extra content (Not rendered)</strong>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">Cancel</el-button>
<el-button type="primary" @click="close">Confirm</el-button>
</div>
</template>
</Dialog>
</template>可拖拽对话框
vue
<script setup lang="ts">
import { useDialog } from 'element-hooks';
const [Dialog, { open, close }] = useDialog({
title: 'Tips',
width: 500,
draggable: true,
});
const [OverflowDialog, { open: openOverflow, close: closeOverflow }] =
useDialog({
title: 'Tips',
width: 500,
draggable: true,
overflow: true,
});
const [
CustomDraggingDialog,
{ open: openCustomDragging, close: closeCustomDragging },
] = useDialog({
title: 'Custom Dragging Style',
width: 500,
draggable: true,
class: 'custom-dragging-style',
});
</script>
<template>
<div class="flex flex-wrap gap-1">
<el-button class="!ml-0" plain @click="open">
Open a draggable Dialog
</el-button>
<el-button class="!ml-0" plain @click="openOverflow">
Open a overflow draggable Dialog
</el-button>
<el-button class="!ml-0" plain @click="openCustomDragging">
Open a custom dragging style Dialog
</el-button>
</div>
<Dialog>
<span>It's a draggable Dialog</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">Cancel</el-button>
<el-button type="primary" @click="close">Confirm</el-button>
</div>
</template>
</Dialog>
<OverflowDialog>
<span>It's a overflow draggable Dialog</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeOverflow">Cancel</el-button>
<el-button type="primary" @click="closeOverflow">Confirm</el-button>
</div>
</template>
</OverflowDialog>
<CustomDraggingDialog>
<span>
This dialog has custom dragging styles. Try dragging it to see the
effects!
</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeCustomDragging">Cancel</el-button>
<el-button type="primary" @click="closeCustomDragging">
Confirm
</el-button>
</div>
</template>
</CustomDraggingDialog>
</template>
<style>
.custom-dragging-style.is-dragging {
border: 2px dashed var(--el-color-primary);
opacity: 0.65;
}
</style>全屏
vue
<script setup lang="ts">
import { useDialog } from 'element-hooks';
const [Dialog, { open, close }] = useDialog({
fullscreen: true,
});
</script>
<template>
<el-button plain @click="open">Open the fullscreen Dialog</el-button>
<Dialog>
<span>It's a fullscreen Dialog</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">Cancel</el-button>
<el-button type="primary" @click="close">Confirm</el-button>
</div>
</template>
</Dialog>
</template>模态框
vue
<script setup lang="ts">
import { useDialog } from 'element-hooks';
const [Dialog, { open, close }] = useDialog({
modal: false,
modalPenetrable: true,
});
</script>
<template>
<el-button plain @click="open">Open the modal Dialog</el-button>
<Dialog>
<span>It's a modal Dialog</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">Cancel</el-button>
<el-button type="primary" @click="close">Confirm</el-button>
</div>
</template>
</Dialog>
</template>自定义动画
vue
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useDialog } from 'element-hooks';
import type { DialogTransition } from 'element-plus';
const currentAnimation = ref('fade');
const isObjectConfig = ref(false);
const transitionConfig = computed<DialogTransition>(() => {
if (isObjectConfig.value) {
return {
name: 'dialog-custom-object',
appear: true,
mode: 'out-in',
duration: 500,
};
}
return `dialog-${currentAnimation.value}`;
});
const [Dialog, { open, close, setState }] = useDialog({
class: 'custom-transition-dialog',
width: '30%',
});
const openDialog = (type: string) => {
currentAnimation.value = type;
isObjectConfig.value = false;
setState({
title: `${type} Animation Dialog`,
transition: `dialog-${type}`,
});
open();
};
const openDialogWithObject = () => {
currentAnimation.value = 'object-config';
isObjectConfig.value = true;
setState({
title: 'object-config Animation Dialog',
transition: transitionConfig.value,
});
open();
};
</script>
<template>
<div>
<el-button plain @click="openDialog('fade')">Default</el-button>
<el-button plain @click="openDialog('scale')">Scale</el-button>
<el-button plain @click="openDialog('slide')">Slide</el-button>
<el-button plain @click="openDialog('bounce')">Bounce</el-button>
<el-button plain @click="openDialogWithObject">Object Config</el-button>
</div>
<Dialog>
<div>
<p>
Current animation: <strong>{{ currentAnimation }}</strong>
</p>
<p>
This dialog demonstrates the {{ currentAnimation }} animation effect.
</p>
<p v-if="isObjectConfig">
<strong>Using object configuration:</strong><br />
<code>{{ JSON.stringify(transitionConfig, null, 2) }}</code>
</p>
</div>
<template #footer>
<el-button @click="close">Cancel</el-button>
<el-button type="primary" @click="close">Confirm</el-button>
</template>
</Dialog>
</template>
<style scoped>
code {
background: var(--el-bg-color-page);
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
display: block;
margin-top: 8px;
}
</style>
<style>
/* Scale Animation */
.dialog-scale-enter-active,
.dialog-scale-leave-active,
.dialog-scale-enter-active .el-dialog,
.dialog-scale-leave-active .el-dialog {
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.dialog-scale-enter-from,
.dialog-scale-leave-to {
opacity: 0;
}
.dialog-scale-enter-from .el-dialog,
.dialog-scale-leave-to .el-dialog {
transform: scale(0.5);
opacity: 0;
}
/* Slide Animation */
.dialog-slide-enter-active,
.dialog-slide-leave-active,
.dialog-slide-enter-active .el-dialog,
.dialog-slide-leave-active .el-dialog {
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.dialog-slide-enter-from,
.dialog-slide-leave-to {
opacity: 0;
}
.dialog-slide-enter-from .el-dialog,
.dialog-slide-leave-to .el-dialog {
transform: translateY(-100px);
opacity: 0;
}
/* Bounce Animation */
.dialog-bounce-enter-active,
.dialog-bounce-leave-active,
.dialog-bounce-enter-active .el-dialog,
.dialog-bounce-leave-active .el-dialog {
transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.dialog-bounce-enter-from,
.dialog-bounce-leave-to {
opacity: 0;
}
.dialog-bounce-enter-from .el-dialog,
.dialog-bounce-leave-to .el-dialog {
transform: scale(0.3) translateY(-50px);
opacity: 0;
}
/* Object Configuration Animation */
.dialog-custom-object-enter-active,
.dialog-custom-object-leave-active,
.dialog-custom-object-enter-active .el-dialog,
.dialog-custom-object-leave-active .el-dialog {
transition: all 0.5s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.dialog-custom-object-enter-from,
.dialog-custom-object-leave-to {
opacity: 0;
}
.dialog-custom-object-enter-from .el-dialog,
.dialog-custom-object-leave-to .el-dialog {
transform: rotate(180deg) scale(0.5);
opacity: 0;
}
</style>Events
vue
<script setup lang="ts">
import { useDialog } from 'element-hooks';
const [Dialog, { open, close }] = useDialog({
modalClass: 'overide-animation',
async beforeClose(done) {
console.log('before-close');
done();
},
});
</script>
<template>
<el-button plain @click="open">Open the event Dialog</el-button>
<Dialog
@open="console.log('open')"
@open-auto-focus="console.log('open-auto-focus')"
@opened="console.log('opened')"
@close="console.log('close')"
@close-auto-focus="console.log('close-auto-focus')"
@closed="console.log('closed')"
>
<span>It's a event Dialog</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">Cancel</el-button>
<el-button type="primary" @click="close">Confirm</el-button>
</div>
</template>
</Dialog>
</template>API
Options
useDialog 的配置项继承自 Element Plus ElDialog 的 Props,但移除了 modelValue(由 Hook 内部管理)。
更多属性请参考 Element Plus Dialog Props。
Controller
| 方法 | 说明 | 参数 |
|---|---|---|
open | 打开对话框 | () => void |
close | 关闭对话框 | () => void |
getVisible | 获取当前对话框的可见状态 | () => boolean |
setState | 动态更新对话框的配置属性 | (state: Partial<DialogOptions>) => void |
setTitle | 快捷更新对话框标题 | (title: string) => void |
instance | 内部 ElDialog 组件实例引用(可调用原生实例方法) | Ref<DialogInstance> |