PopupWrapper.vue 3.54 KB
<template>
<nut-popup
    :visible="visibleModel"
    :position="position"
    :style="popupStyle"
    :closeable="closeable"
    :close-on-click-overlay="closeOnClickOverlay"
    :round="round"
    :z-index="zIndex"
    @close="handleClose"
    @open="handleOpen"
    @opened="handleOpened"
    @closed="handleClosed"
>
    <!-- 自定义头部 -->
    <view v-if="showHeader" class="popup-header">
        <view class="flex items-center justify-between p-4 border-b border-gray-200">
            <view class="text-lg font-medium">{{ title }}</view>
            <view v-if="showCloseButton" @tap="handleClose" class="w-8 h-8 flex items-center justify-center"></view>
        </view>
    </view>

    <!-- 内容区域 -->
    <view class="popup-content" :style="contentStyle">
        <slot></slot>
    </view>
</nut-popup>
</template>

<script setup>
import { ref, computed, defineProps, defineEmits } from 'vue';
import { Close } from '@nutui/icons-vue-taro';

// 定义props
const props = defineProps({
    // 控制弹窗显示/隐藏
    visible: {
        type: Boolean,
        default: false
    },
    // 弹窗位置
    position: {
        type: String,
        default: 'center',
        validator: (value) => ['center', 'top', 'bottom', 'left', 'right'].includes(value)
    },
    // 弹窗标题
    title: {
        type: String,
        default: ''
    },
    // 是否显示头部
    showHeader: {
        type: Boolean,
        default: true
    },
    // 是否显示关闭按钮
    showCloseButton: {
        type: Boolean,
        default: true
    },
    // 是否可关闭
    closeable: {
        type: Boolean,
        default: true
    },
    // 点击遮罩是否关闭
    closeOnClickOverlay: {
        type: Boolean,
        default: false
    },
    // 是否圆角
    round: {
        type: Boolean,
        default: false
    },
    // 层级
    zIndex: {
        type: Number,
        default: 2000
    },
    // 自定义宽度
    width: {
        type: String,
        default: '100%'
    },
    // 自定义高度
    height: {
        type: String,
        default: '100%'
    },
    // 是否全屏
    fullscreen: {
        type: Boolean,
        default: false
    }
});

// 定义emits
const emit = defineEmits(['update:visible', 'close', 'open', 'opened', 'closed']);

// 计算属性处理双向绑定
const visibleModel = computed({
    get() {
        return props.visible;
    },
    set(value) {
        emit('update:visible', value);
    }
});

// 计算弹窗样式
const popupStyle = computed(() => {
    const style = {};

    if (props.fullscreen) {
        style.width = '100%';
        style.height = '100%';
    } else {
        if (props.width) style.width = props.width;
        if (props.height) style.height = props.height;
    }

    return style;
});

// 计算内容区域样式
const contentStyle = computed(() => {
    const style = {};

    if (props.showHeader) {
        style.height = 'calc(100% - 60px)';
    } else {
        style.height = '100%';
    }

    return style;
});

/**
 * 处理关闭事件
 */
const handleClose = () => {
    emit('update:visible', false);
    emit('close');
};

/**
 * 处理打开事件
 */
const handleOpen = () => {
    emit('open');
};

/**
 * 处理已打开事件
 */
const handleOpened = () => {
    emit('opened');
};

/**
 * 处理已关闭事件
 */
const handleClosed = () => {
    emit('closed');
};
</script>

<style lang="less" scoped>
.popup-header {
    background-color: #fff;
    border-bottom: 1px solid #e5e7eb;
}

.popup-content {
    background-color: #f8f9fa;
    overflow-y: auto;
}
</style>