DrawerBox.vue
<template>
<transition name="slide-out" mode="out-in">
<div class="setbox flex flex-col" v-if="visible" ref="slide_box_content">
<div class="title flex flex-between">
<div style="font-weight: bolder">{{ title }}</div>
<CloseOutlined class="cursorPointer" @click="emit('update:visible')" style="color: #9a9fa8" />
</div>
<div class="pt-20 pr-20 pl-20 pb-0 box-conetent" :style="{ width: props.isAutoWidth ? 'auto' : '1200px' }">
<slot></slot>
</div>
<div class="footer">
<slot name="footer"></slot>
</div>
</div>
</transition>
</template>
<script setup>
import { CloseOutlined } from '@ant-design/icons-vue'
import { ref, watch, onBeforeUnmount, provide } from 'vue'
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '标题'
},
isAutoWidth: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:visible', 'closeModal'])
provide('stickyConfig', { offsetHeader: -20 })
const slide_box_content = ref(null)
const clickClose = event => {
if (
event.target !== slide_box_content.value &&
!slide_box_content.value?.contains(event.target) &&
!['text', 'circle'].includes(event.target.nodeName) &&
!['ant-modal'].includes(event.target.parentElement.parentElement.className) &&
!['ant-modal', 'ant-tabs', 'ant-col', 'ant-popover-open', 'a-color', 'ant-modal-wrap ant-modal-centered'].includes(
event.target.className
) &&
!['ant-table-cell-content', 'ant-table-cell'].includes(event.target.parentElement.className) &&
!hasParentWithClass(event.target, 'ant-modal-wrap')
) {
handleCancel()
}
}
const hasParentWithClass = (element, className) => {
while (element.parentElement) {
if (element.parentElement.classList.contains(className)) {
return true // 找到具有指定类名的父元素
}
element = element.parentElement // 继续向上查找
}
return false // 没有找到具有指定类名的父元素
}
const handleCancel = () => {
emit('update:visible')
emit('closeModal')
document.body.removeEventListener('click', clickClose)
}
watch(
() => props.visible,
v => {
if (v) {
setTimeout(() => {
document.body.addEventListener('click', clickClose)
}, 0)
}
}
)
onBeforeUnmount(() => {
document.body.removeEventListener('click', clickClose)
})
</script>
<style lang="less" scoped>
@import '@/assets/css/constant.less';
.setbox {
position: fixed;
z-index: 9999;
top: @NavHeight;
bottom: 0px;
height: 100%;
background: #fff;
box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.1);
right: 0;
padding-bottom: 88px;
max-width: 1200px;
}
.slide-out-enter-from {
transition: all 0.3s;
transform: translateX(100%);
}
/* slide-out过渡 */
.slide-out-enter-active,
.slide-out-leave-active {
transition: all 0.3s;
/* 控制过渡持续时间 */
}
.slide-out-enter,
.slide-out-leave-to {
transform: translateX(100%);
}
.title {
line-height: 56px;
padding: 0px 24px;
color: #333;
font-size: 16px;
font-weight: bold;
background: #f8f9fb;
width: 100%;
}
.box-conetent {
overflow-y: auto;
}
.footer {
display: flex;
justify-content: center;
width: 100%;
margin-top: 20px;
}
</style>
其他文件中使用
<template>
<DrawerBox v-model:visible="state.visible" title="修改密码" isAutoWidth>
<div>
<a-form labelAlign="left" :model="Pwdform" ref="resetPsdRef" :rules="Pwdrules" layout="vertical">
<a-form-item label="原密码" name="oldPwd" class="label_sty">
<a-input-password v-model:value="Pwdform.oldPwd" placeholder="请输入原密码" class="input_sty" />
</a-form-item>
<a-form-item label="设置新密码" name="pwd" class="label_sty">
<a-input-password v-model:value="Pwdform.pwd" placeholder="密码为8~30位的英文、数字、字符组合" class="input_sty" />
</a-form-item>
<a-form-item label="再次输入密码" name="Newpwd" class="label_sty">
<a-input-password v-model:value="Pwdform.Newpwd" placeholder="请再次输入密码" class="input_sty" />
</a-form-item>
</a-form>
</div>
<template #footer>
<a-button type="primary" style="margin-right: 10px" class="confirm_ok long-btn" @click="confirmOk">确定</a-button>
<a-button @click="onClose" class="confirm_close long-btn">取消</a-button>
</template>
</DrawerBox>
</template>
<script setup>
import DrawerBox from '@/components/DrawerBox/index.vue'
const showDrawer = () => {
state.visible = true
}
</script>
效果图