241 lines
6.3 KiB
Vue
241 lines
6.3 KiB
Vue
<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>
|