feat: add layer-closeable prop for modal (#861)

#### What type of PR is this?

/kind feature

#### What this PR does / why we need it:

VModal 组件支持设置是否允许点击蒙层以关闭模态框,并将所有包含表单的模态框设置为不允许点击蒙层关闭。

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/3328

#### Screenshots:

![2023-02-17 12 08 56](https://user-images.githubusercontent.com/21301288/219547318-d7c59742-8546-4bc8-9d49-fcff4053602f.gif)


#### Special notes for your reviewer:

测试方式:

1. 打开 Console 端任意一个表单模态框,测试点击表单外部区域是否会关闭弹框。

#### Does this PR introduce a user-facing change?


```release-note
优化 Console 端部分包含表单的模态框,默认不允许点击外部区域关闭模态框。
```
pull/841/head^2
Ryan Wang 2023-02-17 14:52:13 +08:00 committed by GitHub
parent fad0dcc3ba
commit c8dbd2422c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 39 additions and 3 deletions

View File

@ -90,7 +90,12 @@ const handleClose = () => {
};
</script>
<template>
<VModal :visible="visible" :width="450" @close="handleCancel()">
<VModal
:visible="visible"
:width="450"
:layer-closable="true"
@close="handleCancel()"
>
<div class="flex justify-between items-start py-2 mb-2">
<div class="flex flex-row items-center gap-3">
<component

View File

@ -12,6 +12,7 @@ const props = withDefaults(
bodyClass?: string[];
mountToBody?: boolean;
centered?: boolean;
layerClosable?: boolean;
}>(),
{
visible: false,
@ -22,6 +23,7 @@ const props = withDefaults(
bodyClass: undefined,
mountToBody: false,
centered: true,
layerClosable: false,
}
);
@ -52,6 +54,19 @@ function handleClose() {
emit("close");
}
const focus = ref(false);
function handleClickLayer() {
if (props.layerClosable) {
handleClose();
return;
}
focus.value = true;
setTimeout(() => {
focus.value = false;
}, 300);
}
watch(
() => props.visible,
() => {
@ -85,7 +100,11 @@ watch(
@before-enter="rootVisible = true"
@after-leave="rootVisible = false"
>
<div v-show="visible" class="modal-layer" @click.stop="handleClose()" />
<div
v-show="visible"
class="modal-layer"
@click.stop="handleClickLayer()"
/>
</transition>
<transition
enter-active-class="ease-out duration-200"
@ -98,7 +117,8 @@ watch(
<div
v-show="visible"
:style="contentStyles"
class="modal-content transform transition-all"
class="modal-content transform transition-all duration-300"
:class="{ 'modal-focus': focus }"
>
<div v-if="$slots.header || title" class="modal-header group">
<slot name="header">
@ -163,6 +183,10 @@ watch(
width: calc(100vw - 20px);
max-height: calc(100vh - 5rem);
&.modal-focus {
@apply scale-[1.02];
}
.modal-header {
@apply flex
justify-between

View File

@ -372,6 +372,7 @@ const onVisibleChange = (visible: boolean) => {
:mount-to-body="true"
:width="650"
:centered="false"
:layer-closable="true"
@update:visible="onVisibleChange"
>
<div id="search-input" class="border-b border-gray-100 px-4 py-2.5">

View File

@ -32,6 +32,7 @@ const onVisibleChange = (visible: boolean) => {
:visible="visible"
fullscreen
:title="title"
:layer-closable="true"
@update:visible="onVisibleChange"
>
<template #actions>

View File

@ -75,6 +75,7 @@ const onVisibleChange = (visible: boolean) => {
:visible="visible"
:width="1000"
:mount-to-body="mountToBody"
:layer-closable="true"
height="calc(100vh - 20px)"
@update:visible="onVisibleChange"
>

View File

@ -122,6 +122,7 @@ watch(
:width="750"
title="存储策略"
:body-class="['!p-0']"
:layer-closable="true"
@update:visible="onVisibleChange"
>
<template #actions>

View File

@ -87,6 +87,7 @@ const handleConfirm = () => {
:visible="visible"
:width="1240"
:mount-to-body="true"
:layer-closable="true"
title="选择附件"
height="calc(100vh - 20px)"
@update:visible="onVisibleChange"

View File

@ -29,6 +29,7 @@ const onVisibleChange = (visible: boolean) => {
<VModal
:body-class="['!p-0']"
:visible="visible"
:layer-closable="true"
fullscreen
title="文章预览"
@update:visible="onVisibleChange"

View File

@ -58,6 +58,7 @@
v-model:visible="widgetsModal"
height="calc(100vh - 20px)"
:width="1280"
:layer-closable="true"
title="小组件"
>
<VTabbar