2025-11-11 09:54:39 +08:00

188 lines
4.2 KiB
Vue

<script lang="ts">
import { defineComponent, defineAsyncComponent } from "vue";
export default defineComponent({
name: "CommonModal",
});
</script>
<script lang="ts" setup>
import { ref, watch } from "vue";
import Icon from "@/components/Module/Icon/index.vue";
const emits = defineEmits(["update:open"]);
const asyncContent = defineAsyncComponent({
loader: () => import("@/components/Module/AsyncContent/index.vue"),
delay: 2000,
});
interface propsType {
open: boolean;
title?: string | null;
zIndex?: number | string;
loading?: boolean;
containDom?: any; // 挂载dom
showConfirm?: boolean;
showCancel?: boolean;
confirmText?: string;
cancelText?: string;
onConfirm?: null | (() => void | Promise<void>);
onCancel?: null | (() => void | Promise<void>);
}
let props = withDefaults(defineProps<propsType>(), {
// 默认值
open: false,
title: null,
zIndex: 1000,
loading: false,
confirmText: "确定",
cancelText: "取消",
onConfirm: null,
onCancel: null,
showConfirm: true,
showCancel: true,
});
const useOpen = ref<boolean>(props.open);
const onConfirmHandle = () => {
if (props.onConfirm) {
props.onConfirm();
} else {
emits("update:open", false);
useOpen.value = false;
}
};
const onCancel = () => {
if (props.onCancel) {
props.onCancel();
} else {
emits("update:open", false);
useOpen.value = false;
}
};
watch(
() => props.open,
(val) => {
useOpen.value = val;
},
);
let globalModalRef = ref<HTMLElement>();
const prefix = "_global-modal";
</script>
<template>
<div ref="globalModalRef" :class="prefix">
<a-modal
:zIndex="props.zIndex"
v-bind="$attrs"
:getContainer="() => props.containDom || globalModalRef"
:closable="false"
v-model:open="useOpen"
:footer="null"
@cancel="onCancel"
>
<slot name="header">
<div :class="`${prefix}-head`">
<div :class="`${prefix}-head-label`">
{{ props.title }}
</div>
<button :class="`${prefix}-head-close`" @click="onCancel">
<Icon name="close" />
</button>
</div>
</slot>
<Suspense>
<asyncContent>
<a-spin :spinning="props.loading">
<div :class="`${prefix}-container`">
<div class="modal-content-center">
<asyncContent>
<slot></slot>
</asyncContent>
</div>
</div>
</a-spin>
<slot name="footer">
<div :class="`${prefix}-footer`">
<div :class="`${prefix}-footer__grid`" v-if="props.showCancel">
<a-button @click="onCancel">{{ cancelText }}</a-button>
</div>
<div :class="`${prefix}-footer__grid`" v-if="props.showConfirm">
<a-button type="primary" @click="onConfirmHandle" :loading="props.loading">{{
confirmText
}}</a-button>
</div>
</div>
</slot>
</asyncContent>
<template #fallback>
<div class="modal-content load-box">
<a-spin />
</div>
</template>
</Suspense>
</a-modal>
</div>
</template>
<style lang="scss" scoped>
$prefix: "_global-modal";
.#{$prefix} {
&-head {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-radius: 16px 16px 0 0;
background-color: #d9d9d9;
margin-bottom: 20px;
&-close {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: 500;
color: #333;
}
}
&-container {
padding: 0 16px;
width: 100%;
flex: 1;
align-self: stretch;
}
&-footer {
padding: 12px 16px;
width: 100%;
display: flex;
align-items: center;
justify-content: end;
gap: 20px;
}
:deep(.ant-modal-content) {
padding: 0;
.ant-modal-body {
padding: 0;
display: flex;
align-items: start;
justify-content: start;
flex-direction: column;
}
}
:deep(.ant-spin-nested-loading) {
width: 100%;
}
}
</style>