formPage.vue 5.57 KB
<template>
    <van-popup
        v-model:show="visible"
        position="bottom"
        :style="{ width: '100%', height: '90%' }"
        :close-on-click-overlay="type === 'add' ? false : true"
        :lock-scroll="true"
        :close-on-popstate="true"
        @close="handleClose"
    >
        <div class="info-entry-container">
            <!-- 头部导航栏 -->
            <div class="header">
                <h2 class="title">{{ title }}</h2>
            </div>

            <!-- iframe容器 -->
            <div class="iframe-container" ref="iframeContainer">
                <iframe
                    ref="iframeRef"
                    :key="iframeKey"
                    :src="iframeSrc"
                    frameborder="0"
                    class="form-iframe"
                    @load="handleIframeLoad"
                ></iframe>
            </div>
        </div>
    </van-popup>
</template>

<script setup>
import { ref, watch, nextTick, onMounted, onUnmounted } from 'vue'

/**
 * 组件属性定义
 */
const props = defineProps({
    // 控制弹窗显示状态
    show: {
        type: Boolean,
        default: false
    },
    // iframe的src地址
    iframeSrc: {
        type: String,
        required: true
    },
    title: {
        type: String,
        default: '个人信息录入'
    },
    type: {
        type: String,
        default: 'add'
    }
})

/**
 * 组件事件定义
 */
const emit = defineEmits(['update:show', 'data-received', 'close'])

// 响应式数据
const visible = ref(false)
const iframeRef = ref(null)
const iframeContainer = ref(null)
const iframeKey = ref(0) // 用于强制重新渲染iframe

/**
 * 监听show属性变化,同步更新visible状态
 */
watch(() => props.show, (newVal) => {
    visible.value = newVal
    // 当弹窗显示时,更新iframe的key以强制重新渲染
    if (newVal) {
        iframeKey.value = Date.now()
    }
}, { immediate: true })

/**
 * 监听visible变化,同步更新父组件的show状态
 */
watch(visible, (newVal) => {
    emit('update:show', newVal)
})

/**
 * 处理iframe加载完成事件
 */
const handleIframeLoad = () => {
    nextTick(() => {
        setupMessageListener()
    })
}

/**
 * 调整iframe高度以适应内容
 */
const adjustIframeHeight = () => {
    // 移除高度限制,让iframe自然滚动
    console.log('iframe高度调整已禁用,允许自然滚动')
}

/**
 * 设置消息监听器,用于接收iframe内表单的数据
 */
const setupMessageListener = () => {
    window.addEventListener('message', handleMessage)
}

/**
 * 处理来自iframe的消息
 * @param {MessageEvent} event - 消息事件
 */
const handleMessage = (event) => {
    try {
        // // 验证消息来源(可根据实际情况调整)
        // const allowedOrigins = [
        //     'https://oa-dev.onwall.cn',
        //     'https://oa.onwall.cn',
        //     'http://localhost',
        //     'http://127.0.0.1'
        // ]

        // const isAllowedOrigin = allowedOrigins.some(origin =>
        //     event.origin.startsWith(origin)
        // )

        // if (!isAllowedOrigin) {
        //     console.warn('收到来自未授权源的消息:', event.origin)
        //     return
        // }

        // 解析消息数据
        let messageData = event.data
        if (typeof messageData === 'string') {
            try {
                messageData = JSON.parse(messageData)
            } catch (e) {
                // 如果不是JSON格式,直接使用原始数据
            }
        }

        console.log('收到iframe消息:', messageData)

        // 检查是否是表单提交的数据
        if (messageData && (messageData.type === 'formSubmit')) {
            // 发送数据给父组件
            emit('data-received', messageData)

            // 关闭弹窗
            handleClose()
        }
    } catch (error) {
        console.error('处理iframe消息时出错:', error)
    }
}

/**
 * 处理弹窗关闭
 */
const handleClose = () => {
    visible.value = false
    emit('close')
}

/**
 * 组件挂载时的初始化
 */
onMounted(() => {
    // 如果需要在挂载时就设置监听器
    setupMessageListener()
})

/**
 * 组件卸载时清理
 */
onUnmounted(() => {
    window.removeEventListener('message', handleMessage)
})

/**
 * 暴露给父组件的方法
 */
defineExpose({
    close: handleClose,
    adjustHeight: adjustIframeHeight
})
</script>

<style lang="less" scoped>
.info-entry-container {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    background-color: #f5f5f5;
}

.header {
    display: flex;
    align-items: center;
    padding: 12px 16px;
    background-color: #fff;
    border-bottom: 1px solid #eee;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    position: relative;
    z-index: 10;
}

.close-btn {
    background: none;
    border: none;
    padding: 8px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    transition: background-color 0.2s;

    &:hover {
        background-color: #f0f0f0;
    }

    &:active {
        background-color: #e0e0e0;
    }
}

.title {
    flex: 1;
    text-align: center;
    font-size: 16px;
    font-weight: 500;
    color: #333;
    margin: 0;
}

.iframe-container {
    flex: 1;
    overflow: auto;
    background-color: #fff;
}

.form-iframe {
    width: 100%;
    height: 100%;
    border: none;
    display: block;
}

// 响应式设计
@media (max-width: 768px) {
    .header {
        padding: 10px 12px;
    }

    .title {
        font-size: 14px;
    }

    .close-btn {
        padding: 6px;
    }
}
</style>