You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Preact表单实现三种嵌入触发方式:行内、悬浮气泡、弹窗

实现Preact表单的三种嵌入触发方式

以下是针对你的需求的具体实现方案,基于你已有的RegisterForm组件进行改造,支持行内、悬浮气泡、弹窗三种展示模式:

一、重构基础表单组件

首先修改RegisterForm,移除固定定位样式,让它适配不同容器,并添加可选的关闭回调:

import { useState } from 'preact/hooks';
import PoweredBy from './poweredBy';
import contentSetting from '../mock-data/content-widget.json'

interface RegisterFormProps {
    onClose?: () => void; // 弹窗模式需要关闭功能,设为可选
}

function RegisterForm({ onClose }: RegisterFormProps) {
    const [name, setName] = useState("")
    const [phoneNumber, setPhoneNumber] = useState("")
    const [email, setEmail] = useState("")
    
    const handleInputName = (e) => setName(e.target.value)
    const handleInputPhoneNumber = (e) => setPhoneNumber(e.target.value)
    const handleInputEmail = (e) => setEmail(e.target.value)
    
    const onSubmit = () => {
        const bodyData = { name, phoneNumber, email }
        console.log("提交数据", bodyData)
    }

    return (
        <div className="preact-form-widget max-w-[350px] max-h-[500px] border rounded-xl bg-white">
            {/* 弹窗模式添加关闭按钮 */}
            {onClose && (
                <button onClick={onClose} className="absolute top-2 right-2 text-gray-500 hover:text-gray-700 text-xl">×</button>
            )}
            <div className="px-4 pb-8 relative">
                {contentSetting.businessLogo?.display && (
                    <div className="flex gap-4 h-8 items-center py-4">
                        <img src={contentSetting?.businessLogo?.value} alt="企业logo" className="h-8" />
                        <span className="font-semibold text-sm">{contentSetting?.businessName?.value}</span>
                    </div>
                )}
                <span className="block pb-2 text-2xl font-semibold">{contentSetting.title}</span>
                <p className="pb-4 text-sm text-gray-400 font-normal">{contentSetting.subtitle}</p>
                
                {contentSetting.inputNamePlaceholder && (
                    <div className="relative mb-4">
                        <label htmlFor="name-input" className="absolute top-0 left-4 ease-in-out text-sm text-slate-500">Name</label>
                        <input name="name-input" onInput={handleInputName} className="bg-white py-4 px-4 w-full text-base rounded-xl border border-gray-400 mt-2" />
                    </div>
                )}
                {contentSetting.inputPhoneNumberPlaceholder && (
                    <div className="relative mb-4">
                        <label htmlFor="phone-input" className="absolute top-0 left-4 ease-in-out text-sm text-slate-500">Phone number</label>
                        <input name="phone-input" onInput={handleInputPhoneNumber} className="bg-white py-4 px-4 w-full text-base rounded-xl border border-gray-400 mt-2" />
                    </div>
                )}
                {contentSetting.inputEmailAddressPlaceholder && (
                    <div className="relative">
                        <label htmlFor="email-input" className="absolute top-0 left-4 ease-in-out text-sm text-slate-500">Email address</label>
                        <input name="email-input" onInput={handleInputEmail} className="bg-white py-4 px-4 w-full text-base rounded-xl border border-gray-400 mt-2" />
                    </div>
                )}

                <div className="flex justify-end">
                    <button className="button px-4 py-3 hover:opacity-90 bg-black text-white rounded-[8px] text-base my-10" onClick={onSubmit}>{contentSetting.contentButton}</button>
                </div>
            </div>
            <PoweredBy />
        </div>
    )
}

export default RegisterForm;

二、创建三种展示容器组件

1. 行内表单容器

直接渲染表单,无特殊定位,挂载到页面指定容器:

import RegisterForm from './RegisterForm';

export function InlineForm() {
    return <RegisterForm />;
}

2. 悬浮气泡表单容器

支持自定义悬浮位置,使用fixed定位:

import RegisterForm from './RegisterForm';

interface FloatingFormProps {
    position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
}

export function FloatingForm({ position = 'bottom-right' }: FloatingFormProps) {
    const positionClasses = {
        'bottom-right': 'bottom-4 right-4',
        'bottom-left': 'bottom-4 left-4',
        'top-right': 'top-4 right-4',
        'top-left': 'top-4 left-4',
    };

    return (
        <div className={`fixed z-50 ${positionClasses[position]}`}>
            <RegisterForm />
        </div>
    );
}

3. 弹窗表单容器

带全屏遮罩,支持延迟显示:

import { useState, useEffect } from 'preact/hooks';
import RegisterForm from './RegisterForm';

interface ModalFormProps {
    delay?: number;
}

export function ModalForm({ delay = 3000 }: ModalFormProps) {
    const [isVisible, setIsVisible] = useState(false);

    useEffect(() => {
        const timer = setTimeout(() => setIsVisible(true), delay);
        return () => clearTimeout(timer);
    }, [delay]);

    const handleClose = () => setIsVisible(false);

    if (!isVisible) return null;

    return (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
            <RegisterForm onClose={handleClose} />
        </div>
    );
}

三、编写全局初始化入口

创建初始化脚本,让外部页面可以通过全局函数调用:

import { render } from 'preact';
import { InlineForm } from './InlineForm';
import { FloatingForm } from './FloatingForm';
import { ModalForm } from './ModalForm';

interface FormConfig {
    type: 'inline' | 'floating' | 'modal';
    container?: string; // 行内模式必填:容器选择器
    position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'; // 悬浮模式可选位置
    delay?: number; // 弹窗模式可选延迟时间
}

// 暴露全局函数,供外部页面调用
(window as any).loadPreactForm = (config: FormConfig) => {
    switch (config.type) {
        case 'inline':
            if (!config.container) {
                console.error('行内表单必须指定容器选择器');
                return;
            }
            const container = document.querySelector(config.container);
            if (!container) {
                console.error('指定的容器不存在');
                return;
            }
            render(<InlineForm />, container);
            break;
        case 'floating':
            render(<FloatingForm position={config.position} />, document.body);
            break;
        case 'modal':
            render(<ModalForm delay={config.delay} />, document.body);
            break;
        default:
            console.error('无效的表单类型,可选值:inline/floating/modal');
    }
};

四、打包与使用

打包配置(以Vite为例)

在vite.config.ts中设置输出为UMD格式,支持script标签引入:

import { defineConfig } from 'vite';
import preact from '@preact/preset-vite';

export default defineConfig({
    plugins: [preact()],
    build: {
        lib: {
            entry: './src/init.ts', // 指向上面的初始化脚本
            name: 'PreactFormWidget',
            fileName: (format) => `preact-form-widget.${format}.js`,
        },
        rollupOptions: {
            output: { globals: {} },
        },
    },
});

页面嵌入示例

  1. 行内模式
<!-- 在页面需要显示表单的位置添加容器 -->
<div id="inline-form"></div>

<script src="dist/preact-form-widget.umd.js"></script>
<script>
    loadPreactForm({
        type: 'inline',
        container: '#inline-form'
    });
</script>
  1. 悬浮气泡模式
<script src="dist/preact-form-widget.umd.js"></script>
<script>
    loadPreactForm({
        type: 'floating',
        position: 'bottom-right'
    });
</script>
  1. 弹窗模式
<script src="dist/preact-form-widget.umd.js"></script>
<script>
    loadPreactForm({
        type: 'modal',
        delay: 3000 // 3秒后自动弹出
    });
</script>

内容的提问来源于stack exchange,提问作者Blackmeow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:54:58