2026-01-21 17:13:51 +08:00
|
|
|
<template>
|
2026-01-23 16:20:10 +08:00
|
|
|
<view
|
|
|
|
|
v-if="visible"
|
|
|
|
|
class="drawer"
|
|
|
|
|
:class="{ 'drawer--visible': visible }"
|
|
|
|
|
@click="handleMaskClick"
|
|
|
|
|
>
|
2026-01-21 17:13:51 +08:00
|
|
|
<view class="drawer__content" :style="{ width }" @click.stop>
|
|
|
|
|
<slot></slot>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
visible: Boolean,
|
2026-01-23 16:20:10 +08:00
|
|
|
width: { type: String, default: '600rpx' },
|
|
|
|
|
position: { type: String, default: 'right' } // right, left, top, bottom
|
2026-01-21 17:13:51 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits(['close'])
|
|
|
|
|
|
|
|
|
|
function handleMaskClick() {
|
|
|
|
|
emit('close')
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.drawer {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
z-index: 999;
|
|
|
|
|
|
2026-01-23 16:20:10 +08:00
|
|
|
&::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
background-color: rgba(0, 0, 0, 0.6);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
animation: fadeIn 0.3s ease forwards;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-21 17:13:51 +08:00
|
|
|
&__content {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
background-color: $bg-secondary;
|
|
|
|
|
border-left: 1rpx solid $border-color;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
2026-01-23 16:20:10 +08:00
|
|
|
box-shadow: -4rpx 0 20rpx rgba(0, 0, 0, 0.3);
|
|
|
|
|
animation: slideInRight 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 不同的位置动画
|
|
|
|
|
.drawer--left {
|
|
|
|
|
.drawer__content {
|
|
|
|
|
right: auto;
|
|
|
|
|
left: 0;
|
|
|
|
|
border-left: none;
|
|
|
|
|
border-right: 1rpx solid $border-color;
|
|
|
|
|
animation: slideInLeft 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
|
|
|
box-shadow: 4rpx 0 20rpx rgba(0, 0, 0, 0.3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes fadeIn {
|
|
|
|
|
from { opacity: 0; }
|
|
|
|
|
to { opacity: 1; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes slideInRight {
|
|
|
|
|
from {
|
|
|
|
|
transform: translateX(100%);
|
|
|
|
|
}
|
|
|
|
|
to {
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes slideInLeft {
|
|
|
|
|
from {
|
|
|
|
|
transform: translateX(-100%);
|
|
|
|
|
}
|
|
|
|
|
to {
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 关闭动画 (需要配合Vue transition组件实现更复杂的动画)
|
|
|
|
|
.drawer.drawer--closing {
|
|
|
|
|
&::before {
|
|
|
|
|
animation: fadeOut 0.2s ease forwards;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer__content {
|
|
|
|
|
animation: slideOutRight 0.2s ease forwards;
|
2026-01-21 17:13:51 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-23 16:20:10 +08:00
|
|
|
@keyframes fadeOut {
|
|
|
|
|
from { opacity: 1; }
|
|
|
|
|
to { opacity: 0; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes slideOutRight {
|
|
|
|
|
from {
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
}
|
|
|
|
|
to {
|
|
|
|
|
transform: translateX(100%);
|
|
|
|
|
}
|
2026-01-21 17:13:51 +08:00
|
|
|
}
|
|
|
|
|
</style>
|