perf: 优化抽屉窗口

This commit is contained in:
kuaifan 2025-08-07 14:22:00 +08:00
parent 710609e98b
commit d5a75f887d
4 changed files with 273 additions and 214 deletions

View File

@ -3,24 +3,28 @@
ref="modal" ref="modal"
v-model="show" v-model="show"
:closable="escClosable" :closable="escClosable"
:mask="!isFullscreen" :mask="finalMask"
:mask-closable="maskClosable" :mask-closable="maskClosable"
:footer-hide="true" :footer-hide="true"
:transition-names="transitionNames" :fullscreen="true"
:beforeClose="beforeClose" :class-name="finalClassName"
:class-name="className" :transition-names="finalTransitionNames"
fullscreen> :before-close="beforeClose">
<div v-if="isFullscreen" class="overlay-body">
<slot/>
</div>
<DrawerOverlayView <DrawerOverlayView
v-else :placement="finalPlacement"
:placement="placementName" :size="finalSize"
:size="size" :minSize="finalMinSize"
:minSize="minSize" :resize="finalResize"
:resize="resize"
@on-close="close"> @on-close="close">
<slot/> <template v-if="$slots.title" #title>
<slot name="title"></slot>
</template>
<template v-if="$slots.more" #more>
<slot name="more"></slot>
</template>
<template #default>
<slot></slot>
</template>
</DrawerOverlayView> </DrawerOverlayView>
</Modal> </Modal>
</template> </template>
@ -36,46 +40,55 @@ export default {
type: Boolean, type: Boolean,
default: false default: false
}, },
//
mask: {
default: null
},
//
maskClosable: { maskClosable: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// ESC
escClosable: { escClosable: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 768px
fullscreen: {
default: null
},
// 'right' 'bottom'
// 'bottom'
placement: { placement: {
validator(value) { validator(value) {
return ['right', 'bottom'].includes(value) return ['right', 'bottom'].includes(value)
}, },
default: 'bottom' default: 'bottom'
}, },
forceFullscreen: { // 100%
type: Boolean, //
default: false
},
transitions: {
type: Array,
default: () => []
},
size: { size: {
type: [Number, String], type: [Number, String],
default: "100%" default: "100%"
}, },
// 300px
//
minSize: { minSize: {
type: Number, type: Number,
default: 300 default: 300
}, },
// true
//
resize: { resize: {
type: Boolean, type: Boolean,
default: true default: true
}, },
drawerClass: { //
type: String className: {
},
modalClass: {
type: String type: String
}, },
//
beforeClose: Function beforeClose: Function
}, },
data() { data() {
@ -92,34 +105,55 @@ export default {
}, },
}, },
computed: { computed: {
isFullscreen() { finalFullscreen() {
return this.forceFullscreen || (this.windowWidth < 500 && this.placement != 'bottom') if (typeof this.fullscreen === 'boolean') {
}, return this.fullscreen
placementName() {
return this.isFullscreen ? 'bottom' : this.placement
},
transitionNames() {
if (this.transitions.length > 0) {
return this.transitions
} }
return [`drawer-slide-${this.placementName}`, ''] return this.windowWidth < 768
}, },
className() { finalMask() {
const array = [] if (typeof this.mask === 'boolean') {
if (this.isFullscreen) { return this.mask
array.push("common-drawer-modal") }
if (this.modalClass) { return !this.finalFullscreen
array.push(this.modalClass) },
} finalClassName() {
} else { const array = [
array.push("common-drawer-overlay") "common-drawer",
if (this.drawerClass) { `drawer-${this.finalPlacement}`
array.push(this.drawerClass) ];
} if (this.finalFullscreen) {
array.push(this.placementName) array.push("drawer-fullscreen")
}
if (this.className) {
array.push(this.className)
} }
return array.join(" "); return array.join(" ");
}, },
finalTransitionNames() {
return [`drawer-animation-${this.finalPlacement}`, 'drawer-animation-fade']
},
finalPlacement() {
return this.finalFullscreen ? 'bottom' : this.placement
},
finalSize() {
if (this.finalFullscreen) {
return "100%"
}
return this.size
},
finalMinSize() {
if (this.finalFullscreen) {
return 0
}
return this.minSize
},
finalResize() {
if (this.finalFullscreen) {
return false
}
return this.resize
}
}, },
methods: { methods: {
close() { close() {

View File

@ -1,11 +1,9 @@
<template> <template>
<div ref="body" class="overlay-body" :style="bodyStyle"> <div ref="body" class="overlay-body" :style="bodyStyle">
<div class="overlay-close"> <div class="overlay-close" @click.stop="onClose">
<a href="javascript:void(0)" @click.stop="onClose"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" fill="none" role="img" class="icon fill-current"> <path d="M8.28596 6.51819C7.7978 6.03003 7.00634 6.03003 6.51819 6.51819C6.03003 7.00634 6.03003 7.7978 6.51819 8.28596L11.2322 13L6.51819 17.714C6.03003 18.2022 6.03003 18.9937 6.51819 19.4818C7.00634 19.97 7.7978 19.97 8.28596 19.4818L13 14.7678L17.714 19.4818C18.2022 19.97 18.9937 19.97 19.4818 19.4818C19.97 18.9937 19.97 18.2022 19.4818 17.714L14.7678 13L19.4818 8.28596C19.97 7.7978 19.97 7.00634 19.4818 6.51819C18.9937 6.03003 18.2022 6.03003 17.714 6.51819L13 11.2322L8.28596 6.51819Z" fill="currentColor"></path>
<path d="M8.28596 6.51819C7.7978 6.03003 7.00634 6.03003 6.51819 6.51819C6.03003 7.00634 6.03003 7.7978 6.51819 8.28596L11.2322 13L6.51819 17.714C6.03003 18.2022 6.03003 18.9937 6.51819 19.4818C7.00634 19.97 7.7978 19.97 8.28596 19.4818L13 14.7678L17.714 19.4818C18.2022 19.97 18.9937 19.97 19.4818 19.4818C19.97 18.9937 19.97 18.2022 19.4818 17.714L14.7678 13L19.4818 8.28596C19.97 7.7978 19.97 7.00634 19.4818 6.51819C18.9937 6.03003 18.2022 6.03003 17.714 6.51819L13 11.2322L8.28596 6.51819Z" fill="currentColor"></path> </svg>
</svg>
</a>
</div> </div>
<ResizeLine <ResizeLine
v-if="resize" v-if="resize"
@ -19,6 +17,14 @@
@on-change="onChangeResize"/> @on-change="onChangeResize"/>
<div class="overlay-content"> <div class="overlay-content">
<div class="overlay-content-status"></div> <div class="overlay-content-status"></div>
<div v-if="$slots.title || $slots.more" class="overlay-content-header">
<div class="overlay-content-header-title">
<slot name="title"/>
</div>
<div class="overlay-content-header-more">
<slot name="more"/>
</div>
</div>
<div class="overlay-content-body"><slot/></div> <div class="overlay-content-body"><slot/></div>
<div class="overlay-content-navigation"></div> <div class="overlay-content-navigation"></div>
</div> </div>

View File

@ -1,36 +1,30 @@
body { .common-drawer {
.ivu-modal-wrap { --margin-top: 8px;
&.common-drawer-overlay { --margin-left: 8px;
.ivu-modal { --margin-right: 8px;
.ivu-modal-content { --margin-bottom: 8px;
margin-top: 0; --close-top: 16px;
margin-bottom: 0; --close-right: 16px;
} --close-size: 40px;
} --close-color: #606266;
} --title-color: #303133;
--content-bg-color: #ffffff;
--border-radius: 16px;
&.file-drawer {
--margin-top: 40px;
--margin-left: 0px;
--margin-right: 0px;
--margin-bottom: 0px;
--close-top: 0px;
--close-right: 0px;
--close-color: #ffffff;
--border-radius: 16px 16px 0 0;
} }
}
.common-drawer-overlay { &.approve-drawer {
.ivu-modal { --close-top: 28px;
margin: 0; --close-right: 20px;
padding: 0;
.ivu-modal-content {
background: transparent;
.ivu-modal-close {
display: none;
}
.ivu-modal-body {
padding: 0;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-content: flex-end;
}
}
} }
.overlay-body { .overlay-body {
@ -41,154 +35,204 @@ body {
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
position: relative; position: relative;
z-index: 2;
.overlay-close { .overlay-close {
flex-shrink: 0; position: absolute;
z-index: 2;
top: var(--close-top);
right: var(--close-right);
margin-top: var(--status-bar-height);
width: var(--close-size);
height: var(--close-size);
display: flex; display: flex;
align-items: flex-end; justify-content: center;
justify-content: flex-end; align-items: center;
padding-top: var(--status-bar-height); cursor: pointer;
> a { > svg {
display: flex; color: var(--close-color);
align-items: center; opacity: 0.8;
justify-content: center; width: 24px;
width: 40px; height: 24px;
height: 40px; transition: all 0.3s;
color: #fff; }
.icon {
width: 24px;
height: 24px
}
&:hover {
> svg { > svg {
transition: all 0.3s; opacity: 1;
} transform: rotate(-90deg);
&:hover {
color: #fff;
> svg {
transform: rotate(-90deg);
}
} }
} }
} }
.overlay-resize { .overlay-resize {
width: 100%; position: absolute;
height: 5px; top: 0;
margin-bottom: -5px; left: var(--margin-left);
z-index: 1; right: auto;
bottom: 0;
width: 5px;
z-index: 2;
&.bottom {
left: 0;
right: 0;
top: var(--margin-top);
bottom: 0;
width: 100%;
height: 5px;
}
} }
.overlay-content { .overlay-content {
flex: 1; flex: 1;
min-height: 0;
position: relative; position: relative;
background: #fff; z-index: 1;
border-radius: 18px 18px 0 0; background-color: var(--content-bg-color);
margin: var(--margin-top) var(--margin-right) var(--margin-bottom) var(--margin-left);
border-radius: var(--border-radius);
overflow: hidden;
cursor: default; cursor: default;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.overlay-content-status { &-status {
flex-shrink: 0; flex-shrink: 0;
height: var(--status-bar-height); height: var(--status-bar-height);
} }
.overlay-content-body { &-header {
flex-shrink: 0;
padding: calc(var(--close-top) - var(--margin-top)) calc(var(--close-size) + var(--close-right)) calc(var(--close-top) - var(--margin-top)) 30px;
box-sizing: content-box;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 40px;
gap: 12px;
&-title {
flex: 1;
min-width: 0;
color: var(--title-color);
font-size: 20px;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&-more {
flex-shrink: 0;
}
}
&-body {
flex: 1; flex: 1;
height: 0; min-height: 0;
position: relative; position: relative;
} }
.overlay-content-navigation { &-navigation {
flex-shrink: 0; flex-shrink: 0;
height: var(--navigation-bar-height); height: var(--navigation-bar-height);
} }
.ivu-modal-wrap-apply {
.ivu-modal-wrap-apply-title {
border-top-left-radius: 18px;
}
}
} }
} }
}
.drawer-fullscreen {
--margin-top: 0px;
--margin-left: 0px;
--margin-right: 0px;
--margin-bottom: 0px;
--close-top: 8px;
--close-right: 8px;
--border-radius: 0;
&.approve-drawer {
--close-top: 20px;
--close-right: 12px;
}
}
body {
.ivu-modal-wrap {
&.common-drawer {
overflow: hidden;
.ivu-modal {
.ivu-modal-content {
margin-top: 0;
margin-bottom: 0;
background-color: transparent;
}
.ivu-modal-close {
display: none;
}
&.right {
.ivu-modal {
.ivu-modal-content {
.ivu-modal-body { .ivu-modal-body {
flex-direction: row; padding: 0;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
} }
} }
} }
}
}
.overlay-body { body.dark-mode-reverse {
flex-direction: row; .common-drawer {
&.file-drawer {
.overlay-close { --close-color: #000000;
align-items: flex-start;
}
.overlay-resize {
width: 5px;
height: 100%;
margin-right: -5px;
z-index: 1;
}
.overlay-content {
border-radius: 18px 0 0 18px;
}
} }
} }
} }
.drawer-animation {
&-fade {
&-enter-active,
&-leave-active {
transition: opacity .5s cubic-bezier(0.32, 0.72, 0, 1);
}
.common-drawer-modal { &-enter,
.ivu-modal-fullscreen { &-leave-to {
background-color: #ffffff; opacity: 0;
}
} }
.overlay-body { &-right {
display: flex; &-enter-active,
flex-direction: column; &-leave-active {
width: 100%; touch-action: none;
height: 100%; will-change: transform, opacity;
max-width: 100%; transition: transform .2s cubic-bezier(0.32, 0.72, 0, 1), opacity .2s cubic-bezier(0.32, 0.72, 0, 1);
max-height: 100%; }
position: relative;
z-index: 2; &-enter,
&-leave-to {
transform: translate(15%, 0) scale(0.98);
opacity: 0;
}
}
&-bottom {
&-enter-active,
&-leave-active {
touch-action: none;
will-change: transform, opacity;
transition: transform .2s cubic-bezier(0.32, 0.72, 0, 1), opacity .2s cubic-bezier(0.32, 0.72, 0, 1);
}
&-enter,
&-leave-to {
transform: translate(0, 15%) scale(0.98);
opacity: 0;
}
} }
} }
.drawer-slide-bottom-enter-active {
transition: all .2s ease;
}
.drawer-slide-bottom-leave-active {
transition: all .2s ease;
}
.drawer-slide-bottom-enter,
.drawer-slide-bottom-leave-to {
transform: translate(0, 15%) scale(0.98);
opacity: 0;
}
.drawer-slide-right-enter-active {
transition: all .2s ease;
}
.drawer-slide-right-leave-active {
transition: all .2s ease;
}
.drawer-slide-right-enter,
.drawer-slide-right-leave-to {
transform: translate(15%, 0) scale(0.98);
opacity: 0;
}

View File

@ -824,31 +824,6 @@ body {
opacity: 0; opacity: 0;
} }
/*全局 ivu-modal-wrap*/
body {
.ivu-modal-wrap {
&.common-drawer-overlay {
overflow: hidden;
}
&.common-drawer-modal {
.ivu-modal-body {
padding: 0 !important;
.dialog-wrapper {
&.inde-list {
border-radius: 0
}
}
}
.ivu-modal-close {
z-index: 3;
}
}
}
}
/*全局返回按钮*/ /*全局返回按钮*/
.common-nav-back{ .common-nav-back{
cursor: pointer; cursor: pointer;