vike房东端
38.42 MB · 2025-09-18
该对话框组件是一个基于 Vue3 Composition API + <script setup>
编写的高级模态框,具备以下核心能力:
show()
返回 Promise,便于 await 调用组件采用分层结构:
<teleport to="body">
<Transition name="dialog-fade">
<div class="dialog-overlay">
<div class="dialog">
<div class="dialog-header">...</div>
<div class="dialog-body">...</div>
<div class="dialog-footer">...</div>
</div>
</div>
</Transition>
</teleport>
组件支持三种内容渲染方式,按优先级:
content: string
→ v-html
渲染component: Object
→ 动态组件 + props 传入render: Function
→ 渲染函数组件<component v-else-if="component" :is="component" v-bind="componentProps" />
<component v-else-if="render" :is="{ render }" />
使用 ref
+ reactive
管理状态:
const visible = ref(false)
const isDragging = ref(false)
const maximized = ref(false)
const isFullscreen = ref(props.fullscreen)
const position = reactive({
left: '50%',
top: '50%',
width: ...,
height: ...
})
通过 computed
动态计算样式:
const dialogStyle = computed(() => {
const style = { left, top, width, height, transform, zIndex }
if (maximized.value || isFullscreen.value) {
style.width = '100vw'
style.height = '100vh'
style.borderRadius = '0'
}
return style
})
拖拽逻辑包含:
startDrag()
:记录初始位置和鼠标坐标onDrag()
:计算偏移量 + 百分比转换 + 边界限制stopDrag()
:清理事件监听边界限制算法:
const maxWidth = 100 - (dialogWidth / window.innerWidth) * 50
const minWidth = (dialogWidth / window.innerWidth) * 50
newLeft = Math.min(Math.max(newLeft, minWidth), maxWidth)
通过 localStorage
实现窗口状态持久化:
const storageKey = computed(() => {
if (typeof props.remember === 'string') return `dialog_${props.remember}`
if (props.remember === true) return `dialog_${props.title || 'default'}`
return null
})
const savePosition = () => {
localStorage.setItem(storageKey.value, JSON.stringify({
left: position.left,
top: position.top,
width: position.width,
height: position.height,
maximized: maximized.value,
}))
}
show()
返回 Promise,支持 await
调用:
const show = () => {
visible.value = true
return new Promise((resolve, reject) => {
_resolve = resolve
_reject = reject
})
}
// 使用方
const result = await dialogRef.value.show()
if (result) { /* 确认逻辑 */ }
// utils/dialog.js
import { createApp } from 'vue'
import Dialog from '@/components/Dialog.vue' // 你提供的组件
let zIndex = 9999
export function createDialog(options = {}) {
const app = createApp(Dialog, {
...options,
zIndex: options.zIndex || zIndex++
})
const container = document.createElement('div')
document.body.appendChild(container)
const instance = app.mount(container)
// 返回控制句柄
return {
show: instance.show,
toggleMaximize: instance.toggleMaximize,
close: () => {
instance.visible = false
setTimeout(() => {
app.unmount()
document.body.removeChild(container)
}, 300)
},
instance
}
}
// plugins/dialog.js
import { createDialog } from '@/utils/dialog'
export default {
install(app) {
app.config.globalProperties.$dialog = createDialog
app.provide('$dialog', createDialog) // 供 Composition API 使用
}
}
在 main.js
中注册:
import { createApp } from 'vue'
import App from './App.vue'
import DialogPlugin from './plugins/dialog'
const app = createApp(App)
app.use(DialogPlugin)
app.mount('#app')
// 在 Vue 组件 methods 或 setup 中
methods: {
openProjectSettings() {
this.$dialog({
title: '项目设置',
content: '<p>这里是项目配置面板</p>',
width: 700,
remember: 'project-settings',
buttons: [
{ text: '取消', value: false },
{ text: '保存', value: true, type: 'primary' }
]
}).show().then(confirmed => {
if (confirmed) {
this.saveSettings()
}
})
}
}
或在 <script setup>
中:
import { inject } from 'vue'
const $dialog = inject('$dialog')
const openDialog = () => {
$dialog({
title: '系统通知',
content: '<strong>操作成功!</strong>',
confirmText: '好的',
showFooter: true
}).show()
}
<!-- UserEditForm.vue -->
<template>
<div>
<input v-model="form.name" placeholder="姓名" />
<input v-model="form.role" placeholder="角色" />
</div>
</template>
<script setup>
import { reactive } from 'vue'
const emit = defineEmits(['confirm', 'cancel'])
const form = reactive({ name: '', role: '' })
const onSubmit = () => emit('confirm', { ...form })
const onCancel = () => emit('cancel')
</script>
import UserEditForm from './UserEditForm.vue'
const showDialog = () => {
this.$dialog({
title: '编辑用户',
component: UserEditForm,
componentProps: { initialData: currentUser },
width: 600,
remember: 'user-edit',
draggable: true
}).show().then(result => {
if (result) {
// result 是子组件 emit('confirm', data) 传回的数据
updateUser(result)
}
})
}
const showLoading = () => {
const dlg = this.$dialog({
title: '正在处理...',
content: '<p>请稍候,数据加载中...</p>',
showFooter: false,
closeOnClickOverlay: false,
closeOnEsc: false
})
dlg.show()
// 模拟异步操作
setTimeout(() => {
dlg.close()
}, 3000)
}
v-html
需过滤 XSS,建议优先使用组件注入本组件是一个功能完备、架构清晰、用户体验优秀的 Vue3 模态对话框解决方案。它必须配合 Dialog 工厂函数或全局插件使用,才能实现 this.$dialog({...})
的便捷调用方式。
适用于:
通过合理配置与扩展,可覆盖 90% 以上的对话框使用场景,是 Vue3 生态中值得收藏的实用组件。
38.42 MB · 2025-09-18
288.56MB · 2025-09-18
125.3MB · 2025-09-18