如何为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: {} }, }, }, });
页面嵌入示例
- 行内模式
<!-- 在页面需要显示表单的位置添加容器 --> <div id="inline-form"></div> <script src="dist/preact-form-widget.umd.js"></script> <script> loadPreactForm({ type: 'inline', container: '#inline-form' }); </script>
- 悬浮气泡模式
<script src="dist/preact-form-widget.umd.js"></script> <script> loadPreactForm({ type: 'floating', position: 'bottom-right' }); </script>
- 弹窗模式
<script src="dist/preact-form-widget.umd.js"></script> <script> loadPreactForm({ type: 'modal', delay: 3000 // 3秒后自动弹出 }); </script>
内容的提问来源于stack exchange,提问作者Blackmeow
相关产品推荐
相关产品推荐

