|
@@ -1,45 +1,111 @@
|
|
|
<script setup lang="ts">
|
|
|
import {useCompRef} from "@/utils/useCompRef";
|
|
|
-import {ElDialog, ElIcon} from "element-plus";
|
|
|
+import {ElDialog, ElIcon, useEscapeKeydown} from "element-plus";
|
|
|
import {useVModels} from "@vueuse/core";
|
|
|
// @ts-ignore
|
|
|
import {Close} from '@element-plus/icons-vue'
|
|
|
+import {computed} from "vue";
|
|
|
+import {VNode} from "@vue/runtime-core";
|
|
|
+import sleep from "@/utils/sleep";
|
|
|
|
|
|
const props = withDefaults(
|
|
|
defineProps<{
|
|
|
modelValue: boolean,
|
|
|
- title?: string
|
|
|
+ title?: string,
|
|
|
+ width?: string | number,
|
|
|
+ titleAlign?: 'center' | 'left',
|
|
|
+ titleIcon?: VNode | null,
|
|
|
+ titleIconColor?: string,
|
|
|
+ closeOnClickModal?: boolean,
|
|
|
+ bodyCenter?: boolean
|
|
|
}>(),
|
|
|
- {}
|
|
|
+ {
|
|
|
+ width: '420px',
|
|
|
+ titleAlign: 'center',
|
|
|
+ titleIcon: null,
|
|
|
+ titleIconColor: '',
|
|
|
+ closeOnClickModal: true,
|
|
|
+ bodyCenter: false
|
|
|
+ }
|
|
|
)
|
|
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
+const {modelValue, titleIcon} = useVModels(props, emit)
|
|
|
+const dialogRef = useCompRef(ElDialog)
|
|
|
|
|
|
-const {modelValue} = useVModels(props, emit)
|
|
|
+let elOverlay: HTMLDivElement = null
|
|
|
+
|
|
|
+async function handleMakeClick() {
|
|
|
+ if (!props.closeOnClickModal) {
|
|
|
+ const dialog = (dialogRef.value.dialogContentRef.$el as HTMLDivElement).parentElement
|
|
|
+ dialog.style.transition = 'transform .2s ease-out'
|
|
|
+ dialog.style.transform = 'scale(1.08)'
|
|
|
+ await sleep(200)
|
|
|
+ dialog.style.transform = ''
|
|
|
+ await sleep(200)
|
|
|
+ dialog.style.transition = ''
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleOpened() {
|
|
|
+ elOverlay = (dialogRef.value.dialogContentRef.$el as HTMLDivElement).parentElement.parentElement as HTMLDivElement
|
|
|
+ elOverlay.addEventListener('click', handleMakeClick);
|
|
|
+}
|
|
|
+
|
|
|
+function handleClosed() {
|
|
|
+ if (elOverlay) {
|
|
|
+ elOverlay!.removeEventListener('click', handleMakeClick)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function beforeClose(done) {
|
|
|
+ done()
|
|
|
+}
|
|
|
+
|
|
|
+const dialogStyle = computed(() => {
|
|
|
+ return {
|
|
|
+ '--header-align': props.titleAlign,
|
|
|
+ '--title-icon-color': props.titleIconColor,
|
|
|
+ '--body-center': props.bodyCenter ? 'auto' : '15vh auto 50px auto'
|
|
|
+ }
|
|
|
+})
|
|
|
|
|
|
-const dialogRef = useCompRef(ElDialog)
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
<el-dialog v-model="modelValue"
|
|
|
:title="props.title"
|
|
|
+ :before-close="beforeClose"
|
|
|
class="cy-root"
|
|
|
+ :style="dialogStyle"
|
|
|
+ @opened="handleOpened"
|
|
|
+ @closed="handleClosed"
|
|
|
+ :close-on-click-modal="props.closeOnClickModal"
|
|
|
ref="dialogRef"
|
|
|
:show-close="false"
|
|
|
modal-class="cy_dialog-modal"
|
|
|
+ :width="props.width"
|
|
|
draggable>
|
|
|
<template #header="{close, titleId, titleClass}">
|
|
|
+ <template v-if="titleIcon != null">
|
|
|
+ <el-icon class="title_icon">
|
|
|
+ <component :is="titleIcon"/>
|
|
|
+ </el-icon>
|
|
|
+ </template>
|
|
|
<span :class="titleClass">
|
|
|
{{ props.title }}
|
|
|
</span>
|
|
|
- <div class="cy-message-box_close" @click.stop="close">
|
|
|
+ <button class="cy-message-box_close" @click.stop="close">
|
|
|
<ElIcon>
|
|
|
<Close/>
|
|
|
</ElIcon>
|
|
|
- </div>
|
|
|
+ </button>
|
|
|
</template>
|
|
|
<slot/>
|
|
|
+ <template #footer>
|
|
|
+ <slot name="footer"/>
|
|
|
+ </template>
|
|
|
</el-dialog>
|
|
|
</template>
|
|
|
|
|
@@ -49,36 +115,77 @@ const dialogRef = useCompRef(ElDialog)
|
|
|
.cy_dialog-modal {
|
|
|
--el-overlay-color-lighter: rgba(0, 0, 0, .2);
|
|
|
|
|
|
- .dialog-fade-enter-active {
|
|
|
- animation: anim-open .3s ease !important;
|
|
|
+ &.dialog-fade-enter-active {
|
|
|
+ animation: anim-open .2s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.dialog-fade-leave-active {
|
|
|
+ animation: anim-close .2s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes anim-close {
|
|
|
+ 0% {
|
|
|
+ transform: scale(1);
|
|
|
+ background-color: transparent;
|
|
|
+ }
|
|
|
+
|
|
|
+ 100% {
|
|
|
+ transform: scale(0);
|
|
|
+ background-color: transparent;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@keyframes anim-open {
|
|
|
0% {
|
|
|
+ opacity: 0;
|
|
|
transform: scale(.8);
|
|
|
background-color: transparent;
|
|
|
}
|
|
|
+ 40% {
|
|
|
+ transform: scale(1.08);
|
|
|
+ background-color: transparent;
|
|
|
+ }
|
|
|
80% {
|
|
|
- transform: scale(1.1);
|
|
|
+ transform: scale(0.98);
|
|
|
background-color: transparent;
|
|
|
}
|
|
|
100% {
|
|
|
+ opacity: 1;
|
|
|
transform: scale(1);
|
|
|
+ background-color: transparent;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .el-overlay-dialog {
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.cy-root {
|
|
|
border-radius: 20px;
|
|
|
+ margin: var(--body-center);
|
|
|
|
|
|
header {
|
|
|
border-radius: 20px;
|
|
|
border: 0;
|
|
|
background-color: white;
|
|
|
position: relative;
|
|
|
- text-align: center;
|
|
|
- padding: 10px 16px;
|
|
|
+ text-align: var(--header-align);
|
|
|
+ padding: 12px 16px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
|
|
|
+ .el-dialog__title {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+
|
|
|
+ .title_icon {
|
|
|
+ color: var(--title-icon-color);
|
|
|
+ font-size: 24px;
|
|
|
+ margin-right: 10px;
|
|
|
+ transition: transform .2s ease;
|
|
|
+ }
|
|
|
|
|
|
.cy-message-box_close {
|
|
|
position: absolute;
|
|
@@ -105,5 +212,13 @@ const dialogRef = useCompRef(ElDialog)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .el-dialog__body {
|
|
|
+ padding: 10px 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-dialog__footer {
|
|
|
+ padding: 12px 16px;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|