mirror of https://github.com/halo-dev/halo
refactor: visible condition of modal component (#5078)
#### What type of PR is this? /area console /kind improvement /milestone 2.12.x #### What this PR does / why we need it: 重构 UI 的 Modal 组件,支持通过 v-if 控制是否显示(渲染)。 example: ```vue <script lang="ts" setup> import { ref } from "vue" const visible = ref(false) const modal = ref() function open() { visible.value = true } function close() { modal.value.close() } </script> <template> <button @click="open">Open</button> <VModal v-if="visible" ref="modal" title="test"> <button @click="close">Close</button> </VModal> </template> ``` #### Which issue(s) this PR fixes: Fixes #5077 #### Special notes for your reviewer: 测试方式: 1. cd console && pnpm --filter "./packages/components" storybook 2. 测试 Modal 组件在文档中是否工作正常。 3. 启动 Console 或者 UC。 4. 观察以前页面上的弹框是否工作正常。 #### Does this PR introduce a user-facing change? ```release-note 重构 UI 的 Modal 组件,支持通过 v-if 控制是否显示(渲染)。 ```pull/5128/head
parent
36ebc24aeb
commit
44cb311fac
|
@ -4,6 +4,7 @@ import { VModal } from ".";
|
|||
import { VButton } from "../button";
|
||||
import { IconArrowLeft, IconArrowRight } from "@/icons/icons";
|
||||
import { VSpace } from "../space";
|
||||
import { ref } from "vue";
|
||||
|
||||
const meta: Meta<typeof VModal> = {
|
||||
title: "Modal",
|
||||
|
@ -12,16 +13,20 @@ const meta: Meta<typeof VModal> = {
|
|||
render: (args) => ({
|
||||
components: { VModal, VButton, VSpace, IconArrowLeft, IconArrowRight },
|
||||
setup() {
|
||||
return { args };
|
||||
const modal = ref();
|
||||
return { args, modal };
|
||||
},
|
||||
template: `
|
||||
<VButton type="secondary" @click="args.visible = true">打开</VButton>
|
||||
<VModal
|
||||
v-model:visible="args.visible"
|
||||
ref="modal"
|
||||
v-if="args.visible"
|
||||
:fullscreen="args.fullscreen"
|
||||
:title="args.title"
|
||||
:width="args.width"
|
||||
:mount-to-body="true"
|
||||
:layerClosable="true"
|
||||
@close="args.visible = false"
|
||||
>
|
||||
<template #actions>
|
||||
<span>
|
||||
|
@ -43,7 +48,7 @@ const meta: Meta<typeof VModal> = {
|
|||
<VButton loading type="primary" @click="args.visible = false"
|
||||
>确定
|
||||
</VButton>
|
||||
<VButton @click="args.visible = false">取消</VButton>
|
||||
<VButton @click="modal.close()">取消</VButton>
|
||||
</VSpace>
|
||||
</template>
|
||||
</VModal>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, nextTick, reactive, ref, watch } from "vue";
|
||||
import { computed, nextTick, reactive, ref, watch, onMounted } from "vue";
|
||||
import { IconClose } from "../../icons/icons";
|
||||
import type { UseOverlayScrollbarsParams } from "overlayscrollbars-vue";
|
||||
import { useOverlayScrollbars } from "overlayscrollbars-vue";
|
||||
|
@ -17,7 +17,7 @@ const props = withDefaults(
|
|||
layerClosable?: boolean;
|
||||
}>(),
|
||||
{
|
||||
visible: false,
|
||||
visible: undefined,
|
||||
title: undefined,
|
||||
width: 500,
|
||||
height: undefined,
|
||||
|
@ -34,9 +34,23 @@ const emit = defineEmits<{
|
|||
(event: "close"): void;
|
||||
}>();
|
||||
|
||||
const internalVisible = ref(false);
|
||||
const rootVisible = ref(false);
|
||||
const modelWrapper = ref<HTMLElement>();
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
() => {
|
||||
internalVisible.value = props.visible;
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.visible === undefined) {
|
||||
internalVisible.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
const wrapperClasses = computed(() => {
|
||||
return {
|
||||
"modal-wrapper-fullscreen": props.fullscreen,
|
||||
|
@ -52,10 +66,17 @@ const contentStyles = computed(() => {
|
|||
});
|
||||
|
||||
function handleClose() {
|
||||
emit("update:visible", false);
|
||||
emit("close");
|
||||
internalVisible.value = false;
|
||||
setTimeout(() => {
|
||||
emit("update:visible", false);
|
||||
emit("close");
|
||||
}, 200);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
close: handleClose,
|
||||
});
|
||||
|
||||
const focus = ref(false);
|
||||
|
||||
function handleClickLayer() {
|
||||
|
@ -69,17 +90,6 @@ function handleClickLayer() {
|
|||
}, 300);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
() => {
|
||||
if (props.visible) {
|
||||
nextTick(() => {
|
||||
modelWrapper.value?.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// body scroll
|
||||
const modalBody = ref(null);
|
||||
const reactiveParams = reactive<UseOverlayScrollbarsParams>({
|
||||
|
@ -93,10 +103,13 @@ const reactiveParams = reactive<UseOverlayScrollbarsParams>({
|
|||
});
|
||||
const [initialize, instance] = useOverlayScrollbars(reactiveParams);
|
||||
watch(
|
||||
() => props.visible,
|
||||
() => internalVisible.value,
|
||||
(value) => {
|
||||
if (value) {
|
||||
if (modalBody.value) initialize({ target: modalBody.value });
|
||||
nextTick(() => {
|
||||
modelWrapper.value?.focus();
|
||||
});
|
||||
} else {
|
||||
instance()?.destroy();
|
||||
}
|
||||
|
@ -126,7 +139,7 @@ watch(
|
|||
@after-leave="rootVisible = false"
|
||||
>
|
||||
<div
|
||||
v-show="visible"
|
||||
v-show="internalVisible"
|
||||
class="modal-layer"
|
||||
@click.stop="handleClickLayer()"
|
||||
/>
|
||||
|
@ -140,7 +153,7 @@ watch(
|
|||
leave-to-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<div
|
||||
v-show="visible"
|
||||
v-show="internalVisible"
|
||||
:style="contentStyles"
|
||||
class="modal-content transform transition-all duration-300"
|
||||
:class="{ 'modal-focus': focus }"
|
||||
|
|
Loading…
Reference in New Issue