Files
MrleeAdmin/frontend/admin/src/components/dialog/dialog.vue
T
2026-08-31 11:33:59 +08:00

241 lines
6.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<component
:is="dialogComponent"
:class="customClass"
:destroy-on-close="dialogPropsRef.destroyOnClose ?? true"
:close-on-click-modal="dialogPropsRef.closeOnClickModal ?? false"
:close-on-press-escape="dialogPropsRef.closeOnPressEscape ?? true"
v-model="dialogVisible"
:width="dialogPropsRef.width ?? '60%'"
:title="dialogPropsRef.title ?? '操作'"
:fullscreen="fullscreen"
:show-close="dialogPropsRef.showClose ?? true"
:direction="dialogPropsRef.direction ?? 'rtl'"
:size="dialogPropsRef.width ?? '40%'"
:modal="dialogPropsRef.modal ?? true"
:append-to-body="true"
:draggable="dialogPropsRef.draggable ?? true"
>
<template #header="{ close, titleId, titleClass }">
<div class="el-dialog__header-inner">
<span :id="titleId" :class="titleClass">{{ dialogPropsRef.title ?? '操作' }}</span>
<div class="dialog-header-actions">
<ElButton
v-if="dialogPropsRef.dialogType === 'dialog'"
class="dialog-header-btn"
@click="toggleFullscreen"
link
>
<template #icon>
<Icon :icon="fullscreen ? 'ri:fullscreen-exit-line' : 'ri:fullscreen-line'" />
</template>
</ElButton>
<ElButton class="dialog-header-btn" @click="close" link>
<Icon icon="ri:close-large-line" />
</ElButton>
</div>
</div>
</template>
<div class="m-dialog-body" v-loading="dialogPropsRef.loading ?? false">
<slot></slot>
</div>
<template #footer v-if="dialogPropsRef.footer">
<slot name="footer">
<slot name="prepend-footer"></slot>
<div class="footer-right">
<ElButton v-if="dialogPropsRef.showCancelButton" @click="handleCancel">取消</ElButton>
<slot name="center-footer"></slot>
<ElButton
v-if="dialogPropsRef.showConfirmButton"
type="primary"
@click="handleSubmit"
v-loading="dialogPropsRef.confirmLoading ?? false"
>确定</ElButton
>
</div>
<slot name="append-footer"></slot>
</slot>
</template>
</component>
</template>
<script setup lang="ts">
import { Icon } from '@/components/icon'
import { DialogEmits, MyDialogInstance, MyDialogProps } from './types'
import { ElDialog, ElDrawer } from 'element-plus'
const props = withDefaults(defineProps<MyDialogProps>(), {
loading: false,
confirmLoading: false,
showConfirmButton: true,
showCancelButton: true,
footer: true,
dialogType: 'dialog',
draggable: false
})
const emit = defineEmits<DialogEmits>()
const dialogVisible = ref(false)
const fullscreen = ref(false)
// 根据 dialogType 决定使用哪个组件
const dialogComponent = computed(() => {
return dialogPropsRef.value.dialogType === 'drawer' ? ElDrawer : ElDialog
})
// 根据 dialogType 设置不同的 customClass
const customClass = computed(() => {
const baseClass = dialogPropsRef.value.customClass ?? 'm-dialog'
return dialogPropsRef.value.dialogType === 'drawer'
? `${baseClass} m-drawer`
: `${baseClass} m-dialog-box`
})
const dialogPropsRef = ref<MyDialogProps>(props)
const toggleFullscreen = () => {
fullscreen.value = !fullscreen.value
}
const handleCancel = () => {
dialogVisible.value = false
if (props.onCancel) {
props.onCancel()
} else {
emit('cancel')
}
}
const handleSubmit = () => {
if (props.onConfirm) {
props.onConfirm()
} else {
emit('confirm')
}
}
const methods: MyDialogInstance = {
open() {
dialogVisible.value = true
},
close() {
dialogVisible.value = false
},
// 设置组件状态(props)
setState(state: MyDialogProps) {
return new Promise((resolve) => {
dialogPropsRef.value = {
...dialogPropsRef.value,
...state
}
nextTick(() => {
resolve()
})
})
},
getState(): Promise<MyDialogProps> {
return new Promise((resolve) => {
resolve(dialogPropsRef.value)
})
}
}
onMounted(() => {
emit('register', methods)
})
defineExpose(methods)
</script>
<style lang="scss" scoped>
.el-dialog__header-inner {
display: flex;
justify-content: space-between;
align-items: center;
}
.dialog-header-actions {
display: flex;
align-items: center;
}
.dialog-header-btn {
margin-left: 10px;
padding: 0;
width: 24px;
height: 24px;
:deep(.el-icon) {
font-size: 16px;
}
}
:deep(.el-dialog__header) {
display: block !important;
}
:deep(.el-drawer__header) {
margin-bottom: 0;
}
</style>
<style lang="scss">
// 全局样式,用于区分 dialog 和 drawer 的外观
.m-dialog-box {
padding: 0;
.el-dialog__header {
// background-color: var(--el-color-primary-light-6);
// border-bottom: 1px solid var(--el-border-color);
padding: 15px 20px !important;
}
.m-dialog-body {
background-color: var(--el-bg-color);
padding: 0;
}
.el-dialog__footer {
// background-color: var(--el-color-primary-light-6);
border-top: 1px solid var(--el-border-color);
padding: 15px 20px !important;
display: flex;
justify-content: flex-end;
align-items: center;
}
.el-dialog__body {
padding: 0 !important;
}
}
.m-drawer {
.el-drawer__header {
// background-color: var(--el-color-primary-light-6);
// border-bottom: 1px solid var(--el-border-color);
padding: 15px 20px !important;
margin-bottom: 0 !important;
}
.m-dialog-body {
background-color: var(--el-bg-color);
padding: 0;
}
.el-drawer__footer {
// background-color: var(--el-color-primary-light-6);
border-top: 1px solid var(--el-border-color);
padding: 15px 20px !important;
display: flex;
justify-content: flex-end;
align-items: center;
}
.el-drawer__body {
padding: 0 !important;
}
}
// 底部按钮两端对齐的样式(用于select-dept和select-position组件)
.m-dialog-footer-between {
.el-dialog__footer {
justify-content: space-between;
}
.el-drawer__footer {
justify-content: space-between;
}
}
</style>