v0.10.24: /contact-us/ 一比一复刻(CF7 604 表单/通栏 hero/列宽修复)+ PG 连接池 HMR 泄漏修复
This commit is contained in:
@@ -110,7 +110,7 @@
|
||||
| certification | Certification | 2:文案+按钮;html ×2(证书大全) | |
|
||||
| after-sales-service | After Sales Service | 2:标题;目录+文本长块(table-of-contents) | |
|
||||
| faq | FAQs | 2:标题;目录+10 组 toggle 手风琴 | |
|
||||
| contact-us | Contact Us | 1:标题+地图/联系方式 ×9 列表+社媒+表单(CF7 表单→改造) | |
|
||||
| contact-us | Contact Us | 1:标题+地图/联系方式 ×9 列表+社媒+表单(CF7 表单→改造) | v0.10.24 ✅ hero 通栏+表单卡叠压;CF7 604 忠实复刻(两列字段/下拉/acceptance/reCAPTCHA/spinner 占位),提交走 submitInquiry;地址列 #F5F7FF+pin 图标+社交;桌面差 ≤2.3px、移动列差 ≤1px |
|
||||
| distributor | Distributor | 2:图+文+按钮;图区 | |
|
||||
| login | Login | 1:登录注册表单 | |
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dusuniot-rebuild",
|
||||
"version": "0.10.23",
|
||||
"version": "0.10.24",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { Component, createContext, useContext, Fragment, useEffect, useState, type CSSProperties, type ReactNode } from "react";
|
||||
import { getLatestPosts, getLatestProducts, type LatestItem, type PostCardItem } from "~/server/data";
|
||||
import TickerBar from "~/components/website/TickerBar";
|
||||
import { InquiryForm } from "~/components/website/InquiryForm";
|
||||
import { Cf7Form604 } from "~/components/website/Cf7Form604";
|
||||
import { localSiteUrl } from "~/utils/media";
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "~/components/ui/accordion";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
|
||||
@@ -384,14 +384,22 @@ function collectResponsiveCss(elements: ElElement[]): string {
|
||||
if (fsM) css += `@media (max-width:767px){${sel} .el-heading-title{font-size:${fsM} !important;}}\n`;
|
||||
}
|
||||
}
|
||||
// 列响应式宽度(_inline_size_tablet / _inline_size_mobile;页脚内层四列 43%/100%)
|
||||
const tw = st._inline_size_tablet;
|
||||
const mw = st._inline_size_mobile;
|
||||
if (typeof tw === "number" && tw > 0) {
|
||||
// 列响应式宽度(_inline_size_tablet / _inline_size_mobile;页脚内层四列 43%/100%)。
|
||||
// Elementor 语义:桌面 _inline_size 仅 ≥768 生效(原站 post-840.css 的 min-width:768 门),
|
||||
// 未设 _inline_size_mobile 的列在 ≤767 回退 100%(Elementor frontend.css 的
|
||||
// @media(max-width:767px){.elementor-column{width:100%}})——联系页 60/40 两列移动端堆叠。
|
||||
// 值既有数字也有字符串(联系页 _inline_size="60"),统一转数值。
|
||||
const num = (v: unknown) => (typeof v === "number" ? v : v != null && v !== "" ? Number(v) : NaN);
|
||||
const tw = num(st._inline_size_tablet);
|
||||
const mw = num(st._inline_size_mobile);
|
||||
const dw = num(st._inline_size);
|
||||
if (Number.isFinite(tw) && tw > 0) {
|
||||
css += `@media (max-width:1024px){${sel}{flex:0 0 ${tw}% !important;max-width:${tw}% !important;}}\n`;
|
||||
}
|
||||
if (typeof mw === "number" && mw > 0) {
|
||||
if (Number.isFinite(mw) && mw > 0) {
|
||||
css += `@media (max-width:767px){${sel}{flex:0 0 ${mw}% !important;max-width:${mw}% !important;}}\n`;
|
||||
} else if (Number.isFinite(dw) && dw > 0) {
|
||||
css += `@media (max-width:767px){${sel}{flex:0 0 100% !important;max-width:100% !important;}}\n`;
|
||||
}
|
||||
// icon-list 响应式项间距(space_between_tablet/mobile:页脚社交 10 → 7px)
|
||||
if (s(el.widgetType) === "icon-list") {
|
||||
@@ -404,6 +412,11 @@ function collectResponsiveCss(elements: ElElement[]): string {
|
||||
if (smHalf) {
|
||||
css += `@media (max-width:767px){${inlineItems}{margin-inline:-${smHalf} !important;}${inlineItems} > .elementor-icon-list-item{margin-inline:${smHalf} !important;}}\n`;
|
||||
}
|
||||
// 移动端行高(icon_typography_line_height_mobile:联系页地址列 1.25rem,post-840.css 生成规则)
|
||||
const lhM = sizeCss(st.icon_typography_line_height_mobile);
|
||||
if (lhM) {
|
||||
css += `@media (max-width:767px){${sel} .elementor-icon-list-item > .elementor-icon-list-text,${sel} .elementor-icon-list-item > a{line-height:${lhM} !important;}}\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
function walk(els: ElElement[]) {
|
||||
@@ -501,6 +514,11 @@ function SectionNode({ el }: { el: ElElement }) {
|
||||
const bc = s(settings.border_color);
|
||||
if (bc) style.borderColor = bc;
|
||||
}
|
||||
// section 阴影(Elementor box_shadow 控件;联系页 5ed2d55 = 0 10px 50px rgba(71,77,255,.2))
|
||||
const sh = settings.box_shadow_box_shadow as { horizontal?: number; vertical?: number; blur?: number; spread?: number; color?: string } | undefined;
|
||||
if (sh && (sh.blur || sh.spread || sh.horizontal || sh.vertical)) {
|
||||
style.boxShadow = `${sh.horizontal || 0}px ${sh.vertical || 0}px ${sh.blur || 0}px ${sh.spread || 0}px ${sh.color || "rgba(0,0,0,0.5)"}`;
|
||||
}
|
||||
// 列内容垂直对齐(content_position=middle → align-items:center;页脚 d834160 实测)
|
||||
const contentPos = s(settings.content_position);
|
||||
if (contentPos === "middle") inner.alignItems = "center";
|
||||
@@ -547,8 +565,10 @@ function boxNonZero(v: unknown): boolean {
|
||||
function ColumnNode({ el, gapPad, contentPos }: { el: ElElement; gapPad?: string; contentPos?: string }) {
|
||||
const settings = resolveGlobals(el.settings || {});
|
||||
const outer: CSSProperties = { minWidth: 0, flex: "1 1 0" };
|
||||
const inline = settings._inline_size;
|
||||
if (typeof inline === "number" && inline > 0) {
|
||||
// _inline_size 既有数字也有字符串(联系页 ec3c8ff = "60",原站生成 width:60%),统一转数值
|
||||
const inlineRaw = settings._inline_size;
|
||||
const inline = typeof inlineRaw === "number" ? inlineRaw : inlineRaw != null && inlineRaw !== "" ? Number(inlineRaw) : NaN;
|
||||
if (Number.isFinite(inline) && inline > 0) {
|
||||
outer.flex = `0 0 ${inline}%`;
|
||||
outer.maxWidth = `${inline}%`;
|
||||
}
|
||||
@@ -753,7 +773,7 @@ function WidgetNodeInner({ el }: { el: ElElement }) {
|
||||
case "social-icons":
|
||||
return <SocialIconsWidget settings={settings} />;
|
||||
case "eael-contact-form-7":
|
||||
return <ContactFormWidget settings={settings} />;
|
||||
return <ContactFormWidget el={el} settings={settings} />;
|
||||
case "video":
|
||||
return <VideoWidget settings={settings} />;
|
||||
case "counter":
|
||||
@@ -1133,11 +1153,49 @@ function SocialIconsWidget({ settings }: { settings: Record<string, unknown> })
|
||||
}
|
||||
|
||||
|
||||
/** # 组件 eael-contact-form-7(EAEL Contact Form 7 皮肤):渲染内置询盘表单 */
|
||||
function ContactFormWidget({ settings }: { settings: Record<string, unknown> }) {
|
||||
/** # 组件 eael-contact-form-7(EAEL Contact Form 7 皮肤):渲染 CF7 604 忠实表单
|
||||
* DOM 层级仿 EAEL 输出:eael-contact-form-7-wrapper > eael-contact-form.eael-contact-form-7 > wpcf7 > form。
|
||||
* 控件样式按原站 post-840.css 生成规则 + widget 设置生成节点级 <style>
|
||||
* (字段 1px #C9CEE8 边框/圆角 3、textarea 134px、select #efefef/#757575、提交 #ED6C00→#FFA11B)。
|
||||
* 基础规则用 :where() 降特异性(0,0,0),保证页面注入的 custom_css
|
||||
* (.ele-new-form4-dif 的 p 两列浮动布局、submit 全宽等)与响应式规则优先生效。
|
||||
*/
|
||||
function ContactFormWidget({ el, settings }: { el: ElElement; settings: Record<string, unknown> }) {
|
||||
const sid = el.id;
|
||||
const fieldBorder = s(settings.field_border_color) || "#C9CEE8";
|
||||
const taH = sizeCss(settings.textarea_height) || "134px";
|
||||
const btnRadius = paddingCss(settings.button_border_radius) || "3px";
|
||||
const css = `
|
||||
[data-el="${sid}"] :where(.wpcf7-form p){margin:0 0 20px}
|
||||
[data-el="${sid}"] :where(.wpcf7-form label){display:block;margin-bottom:0;line-height:1;font-size:16px;color:#333}
|
||||
[data-el="${sid}"] :where(.wpcf7-form-control-wrap){display:block}
|
||||
[data-el="${sid}"] :where(.wpcf7-text,.wpcf7-email,.wpcf7-tel,.wpcf7-select,.wpcf7-textarea){width:100%;height:42px;padding:8px 16px;margin:0;border:1px solid ${fieldBorder};border-radius:3px;background:#fff;color:#000;font-size:16px;line-height:24px;font-family:inherit}
|
||||
[data-el="${sid}"] :where(.wpcf7-select){padding:10px 12px 9px;background:#efefef;color:#757575}
|
||||
[data-el="${sid}"] :where(.wpcf7-textarea){height:${taH};line-height:1.5;resize:vertical}
|
||||
[data-el="${sid}"] :where(input[type=submit]){width:100%;height:44px;margin:10px 0 0;padding:10px 20px;border:0;border-radius:${btnRadius};background:#ED6C00;color:#fff;font-size:16px;line-height:24px;cursor:pointer;transition:background .25s linear}
|
||||
[data-el="${sid}"] :where(input[type=submit]:hover){background:#FFA11B}
|
||||
[data-el="${sid}"] :where(.wpcf7-list-item){display:inline-block;margin:0 !important}
|
||||
[data-el="${sid}"] :where(.wpcf7-list-item > label){display:block;font-size:14px}
|
||||
[data-el="${sid}"] :where(.wpcf7-list-item input[type=checkbox]){margin:0 0 3px;vertical-align:middle}
|
||||
[data-el="${sid}"] :where(.wpcf7-list-item-label){color:#9396A1;font-size:0.875rem;line-height:1}
|
||||
[data-el="${sid}"] :where(.wpcf7-list-item-label a){color:#9396A1}
|
||||
[data-el="${sid}"] :where(.ds-recaptcha-note){color:#9396A1;font-size:0.875rem}
|
||||
[data-el="${sid}"] :where(.ds-recaptcha-note a){color:#9396A1}
|
||||
[data-el="${sid}"] :where(.ds-submit-p){margin:0 !important}
|
||||
[data-el="${sid}"] :where(.wpcf7-spinner){display:inline-block;position:relative;visibility:hidden;opacity:0.75;width:24px;height:24px;margin:0 24px;border:none;border-radius:100%;background-color:#23282d}
|
||||
[data-el="${sid}"] :where(.wpcf7-response-output){margin:0 0.5em 2em;padding:0.2em 1em;border:2px solid transparent;font-size:16px}
|
||||
[data-el="${sid}"] :where(.wpcf7-response-output.ds-ok){border-color:#46b450}
|
||||
[data-el="${sid}"] :where(.wpcf7-response-output.ds-err){border-color:#dc3232}
|
||||
[data-el="${sid}"] :where(.wpcf7-response-output[aria-hidden="true"]){display:none}
|
||||
`;
|
||||
return (
|
||||
<div className="mb-[18px] rounded-md bg-white px-7 py-[26px]">
|
||||
<InquiryForm formKey="contact-us-form" title={undefined} />
|
||||
<div className="eael-contact-form-7-wrapper">
|
||||
<style>{css}</style>
|
||||
<div className="eael-contact-form eael-contact-form-7">
|
||||
<div className="wpcf7">
|
||||
<Cf7Form604 formKey="contact-form-1" spinner />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ const SETTING_KEYS = new Set([
|
||||
"icon_typography_typography",
|
||||
"icon_typography_font_size",
|
||||
"icon_typography_font_size_tablet",
|
||||
"icon_typography_line_height_mobile",
|
||||
"icon_typography_font_weight",
|
||||
"gradient_angle",
|
||||
"color_a",
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* # 组件 CF7 表单 604(Contact Us Form)忠实复刻
|
||||
* 字段与 DOM 结构取自 legacy dump `wpcf7_contact_form` 604 的 post_content 与原站渲染 HTML:
|
||||
* Name / Company / Business email / Phone number / Product / Interest / Annual Demand(下拉)/
|
||||
* Message*(textarea)+ 6 个 UTM 隐藏字段 + acceptance 勾选 + reCAPTCHA 声明 + Submit。
|
||||
* 提交走 submitInquiry(form_key=contact-form-1);成功/失败显示 wpcf7-response-output。
|
||||
* 字段两列/三列/全宽布局由外层样式域(.ele-new-form4-dif,页面 custom_css)提供,
|
||||
* 本组件只输出 CF7 的 form 层,外层由调用方包 .eael-contact-form-7 / .wpcf7 结构。
|
||||
*/
|
||||
import { useState, type FormEvent } from "react";
|
||||
import { submitInquiry } from "~/server/inquiries";
|
||||
|
||||
/** # 常量 产品线下拉(CF7 表单 604 原文) **/
|
||||
export const CF7_PRODUCT_OPTIONS = ["IoT Gateway", "System on Module", "Development Board", "Remote Control", "Voice Recognition Module", "Control Board", "Beacon & Sensor", "Other"];
|
||||
/** # 常量 合作意向下拉(CF7 表单 604 原文) **/
|
||||
export const CF7_INTEREST_OPTIONS = ["Sales", "Distribution", "ODM", "OEM", "Technical support", "Other"];
|
||||
/** # 常量 年需求量下拉(CF7 表单 604 原文) **/
|
||||
export const CF7_DEMAND_OPTIONS = ["1-50", "51-200", "201-1000", "1001-5000", "5000+"];
|
||||
|
||||
/** # 组件 CF7 604 表单(form 层)
|
||||
* spinner:联系页 CF7 渲染了 spinner 占位(submit 段高 74),弹窗 1129 无(44)——按调用方开关。
|
||||
*/
|
||||
export function Cf7Form604(props: { formKey?: string; spinner?: boolean }) {
|
||||
const [status, setStatus] = useState<"idle" | "submitting" | "success" | "error">("idle");
|
||||
const [errorMsg, setErrorMsg] = useState("");
|
||||
|
||||
/** # 函数 表单提交:映射到 submitInquiry(form_key=contact-form-1,legacy 604) */
|
||||
async function handleSubmit(e: FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
// currentTarget 在 await 之后会被 React 置空,先留引用
|
||||
const formEl = e.currentTarget;
|
||||
const fd = new FormData(formEl);
|
||||
setStatus("submitting");
|
||||
setErrorMsg("");
|
||||
try {
|
||||
const res = await submitInquiry({
|
||||
data: {
|
||||
formKey: props.formKey || "contact-form-1",
|
||||
name: String(fd.get("your-name") || ""),
|
||||
email: String(fd.get("your-email") || ""),
|
||||
phone: String(fd.get("Phone") || ""),
|
||||
company: String(fd.get("Company") || ""),
|
||||
country: "",
|
||||
message: String(fd.get("your-message") || ""),
|
||||
productInterest: String(fd.get("Product") || ""),
|
||||
_gotcha: String(fd.get("_gotcha") || ""),
|
||||
extra: {
|
||||
interest: String(fd.get("Interest") || ""),
|
||||
annualdemand: String(fd.get("AnnualDemand") || ""),
|
||||
consent: fd.get("acceptance-918") ? "1" : "",
|
||||
},
|
||||
},
|
||||
});
|
||||
if (res.ok) {
|
||||
setStatus("success");
|
||||
formEl.reset();
|
||||
} else {
|
||||
setStatus("error");
|
||||
setErrorMsg(res.error || "提交失败,请稍后重试");
|
||||
}
|
||||
} catch (err) {
|
||||
setStatus("error");
|
||||
setErrorMsg(err instanceof Error ? err.message : "网络异常,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="wpcf7-form init" noValidate onSubmit={handleSubmit}>
|
||||
{/* 蜜罐字段:隐藏,机器人填写即视为垃圾提交(原站无此字段) */}
|
||||
<input type="text" name="_gotcha" tabIndex={-1} autoComplete="off" className="hidden" aria-hidden="true" />
|
||||
<p>
|
||||
<label>
|
||||
Name*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="your-name">
|
||||
<input type="text" name="your-name" required className="wpcf7-form-control wpcf7-text wpcf7-validates-as-required" maxLength={400} />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Company*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="Company">
|
||||
<input type="text" name="Company" required className="wpcf7-form-control wpcf7-text wpcf7-validates-as-required" maxLength={400} />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Business email*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="your-email">
|
||||
<input type="email" name="your-email" required className="wpcf7-form-control wpcf7-email wpcf7-validates-as-required wpcf7-text" maxLength={400} />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Phone number
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="Phone">
|
||||
<input type="tel" name="Phone" className="wpcf7-form-control wpcf7-tel wpcf7-text" maxLength={400} />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Product*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="Product">
|
||||
<select name="Product" required className="wpcf7-form-control wpcf7-select wpcf7-validates-as-required">
|
||||
<option value="">—Please choose an option—</option>
|
||||
{CF7_PRODUCT_OPTIONS.map((o) => (
|
||||
<option key={o} value={o}>{o}</option>
|
||||
))}
|
||||
</select>
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Interest*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="Interest">
|
||||
<select name="Interest" required className="wpcf7-form-control wpcf7-select wpcf7-validates-as-required">
|
||||
<option value="">—Please choose an option—</option>
|
||||
{CF7_INTEREST_OPTIONS.map((o) => (
|
||||
<option key={o} value={o}>{o}</option>
|
||||
))}
|
||||
</select>
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Annual Demand*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="AnnualDemand">
|
||||
<select name="AnnualDemand" required className="wpcf7-form-control wpcf7-select wpcf7-validates-as-required">
|
||||
<option value="">—Please choose an option—</option>
|
||||
{CF7_DEMAND_OPTIONS.map((o) => (
|
||||
<option key={o} value={o}>{o}</option>
|
||||
))}
|
||||
</select>
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Message*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="your-message">
|
||||
<textarea name="your-message" required className="wpcf7-form-control wpcf7-textarea" rows={6} maxLength={2000} placeholder="Leave us a message about your project or product needs" />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
{/* UTM 隐藏字段(CF7 604 原文,跨度归因用) */}
|
||||
<p><input type="hidden" name="utm_campaign" defaultValue="" /></p>
|
||||
<p><input type="hidden" name="utm_source" defaultValue="" /></p>
|
||||
<p><input type="hidden" name="utm_medium" defaultValue="" /></p>
|
||||
<p><input type="hidden" name="utm_term" defaultValue="" /></p>
|
||||
<p><input type="hidden" name="utm_content" defaultValue="" /></p>
|
||||
<p><input type="hidden" name="gclid" defaultValue="" /></p>
|
||||
<p>
|
||||
<span className="wpcf7-form-control-wrap" data-name="acceptance-918">
|
||||
<span className="wpcf7-form-control wpcf7-acceptance optional">
|
||||
<span className="wpcf7-list-item">
|
||||
<label>
|
||||
<input type="checkbox" name="acceptance-918" value="1" defaultChecked />
|
||||
{/* 原站 checkbox 与文本为行内流式排版(非 flex),文本占满整行宽(608px)换 3 行 */}
|
||||
{" "}
|
||||
<span className="wpcf7-list-item-label">
|
||||
By checking this box, you consent to receive commercial messages such as newsletters, event invitations, promotional and educational content,
|
||||
product updates, transaction-related emails, and customer service emails, from DusunIoT, in accordance with our{" "}
|
||||
<a href="/privacy-policy/">Privacy Policy</a>. You may withdraw your consent at any time.
|
||||
</span>
|
||||
</label>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<p className="ds-recaptcha-note">
|
||||
This site is protected by reCAPTCHA and the Google <a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Privacy Policy</a> and{" "}
|
||||
<a href="https://policies.google.com/terms" target="_blank" rel="noopener noreferrer">Terms of Service</a> apply.
|
||||
</p>
|
||||
<p className="ds-submit-p">
|
||||
<input className="wpcf7-form-control wpcf7-submit" type="submit" value={status === "submitting" ? "Submitting…" : "Submit"} disabled={status === "submitting"} />
|
||||
{props.spinner ? <span className="wpcf7-spinner" aria-hidden="true" /> : null}
|
||||
</p>
|
||||
<div className={"wpcf7-response-output" + (status === "success" ? " ds-ok" : status === "error" ? " ds-err" : "")} aria-hidden={status === "idle"}>
|
||||
{status === "success" ? "Thank you for your message. It has been sent." : status === "error" ? errorMsg : ""}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -10,8 +10,8 @@
|
||||
* + acceptance 勾选 + reCAPTCHA 声明 + Submit)
|
||||
* - popup 1129 生成 CSS(字段 48%/31.3% 两列三列布局、边框 #C9CEE8、按钮 #ED6C00→#FFA11B)
|
||||
*/
|
||||
import { useEffect, useRef, useState, type FormEvent } from "react";
|
||||
import { submitInquiry } from "~/server/inquiries";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Cf7Form604 } from "~/components/website/Cf7Form604";
|
||||
|
||||
/** # 常量 展开的弹窗 id(原站 popup 1129) **/
|
||||
const POPUP_ID = "1129";
|
||||
@@ -25,19 +25,11 @@ const POPUP_SOCIALS = [
|
||||
{ icon: "fa-twitter", label: "Twitter", href: "https://twitter.com/Dusunelectron", color: "#1DA1F2" },
|
||||
];
|
||||
|
||||
/** # 常量 产品线下拉(CF7 表单 604 原文) **/
|
||||
const PRODUCT_OPTIONS = ["IoT Gateway", "System on Module", "Development Board", "Remote Control", "Voice Recognition Module", "Control Board", "Beacon & Sensor", "Other"];
|
||||
/** # 常量 合作意向下拉 **/
|
||||
const INTEREST_OPTIONS = ["Sales", "Distribution", "ODM", "OEM", "Technical support", "Other"];
|
||||
/** # 常量 年需求量下拉 **/
|
||||
const DEMAND_OPTIONS = ["1-50", "51-200", "201-1000", "1001-5000", "5000+"];
|
||||
/** # 常量 产品线下拉/合作意向/年需求量改由 Cf7Form604 提供(CF7 表单 604 原文) **/
|
||||
|
||||
/** # 组件 询价弹窗(全局挂载于 Layout,监听 Elementor popup 链接) */
|
||||
export function InquiryPopup() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [status, setStatus] = useState<"idle" | "submitting" | "success" | "error">("idle");
|
||||
const [errorMsg, setErrorMsg] = useState("");
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
// 全局委托:拦截任意指向 popup 1129 的 Elementor 动作链接
|
||||
useEffect(() => {
|
||||
@@ -57,8 +49,6 @@ export function InquiryPopup() {
|
||||
}
|
||||
if (id !== POPUP_ID) return;
|
||||
e.preventDefault();
|
||||
setStatus("idle");
|
||||
setErrorMsg("");
|
||||
setOpen(true);
|
||||
};
|
||||
document.addEventListener("click", onClick);
|
||||
@@ -81,44 +71,6 @@ export function InquiryPopup() {
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
/** # 函数 表单提交:映射到 submitInquiry(form_key=contact-form-1,legacy 604) */
|
||||
async function handleSubmit(e: FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
const fd = new FormData(e.currentTarget);
|
||||
setStatus("submitting");
|
||||
setErrorMsg("");
|
||||
try {
|
||||
const res = await submitInquiry({
|
||||
data: {
|
||||
formKey: "contact-form-1",
|
||||
name: String(fd.get("your-name") || ""),
|
||||
email: String(fd.get("your-email") || ""),
|
||||
phone: String(fd.get("Phone") || ""),
|
||||
company: String(fd.get("Company") || ""),
|
||||
country: "",
|
||||
message: String(fd.get("your-message") || ""),
|
||||
productInterest: String(fd.get("Product") || ""),
|
||||
_gotcha: String(fd.get("_gotcha") || ""),
|
||||
extra: {
|
||||
interest: String(fd.get("Interest") || ""),
|
||||
annualdemand: String(fd.get("AnnualDemand") || ""),
|
||||
consent: fd.get("acceptance-918") ? "1" : "",
|
||||
},
|
||||
},
|
||||
});
|
||||
if (res.ok) {
|
||||
setStatus("success");
|
||||
formRef.current?.reset();
|
||||
} else {
|
||||
setStatus("error");
|
||||
setErrorMsg(res.error || "提交失败,请稍后重试");
|
||||
}
|
||||
} catch (err) {
|
||||
setStatus("error");
|
||||
setErrorMsg(err instanceof Error ? err.message : "网络异常,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="ds-inquiry-popup elementor-popup-modal dialog-type-lightbox"
|
||||
@@ -144,119 +96,7 @@ export function InquiryPopup() {
|
||||
<h3 className="eael-contact-form-title">CONTACT US</h3>
|
||||
</div>
|
||||
<div className="wpcf7">
|
||||
<form ref={formRef} className="wpcf7-form init" onSubmit={handleSubmit}>
|
||||
<p>
|
||||
<label>
|
||||
Name*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="your-name">
|
||||
<input type="text" name="your-name" required className="wpcf7-form-control wpcf7-text wpcf7-validates-as-required" maxLength={400} />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Company*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="Company">
|
||||
<input type="text" name="Company" required className="wpcf7-form-control wpcf7-text wpcf7-validates-as-required" maxLength={400} />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Business email*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="your-email">
|
||||
<input type="email" name="your-email" required className="wpcf7-form-control wpcf7-text wpcf7-email wpcf7-validates-as-required" maxLength={400} />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Phone number
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="Phone">
|
||||
<input type="tel" name="Phone" className="wpcf7-form-control wpcf7-text wpcf7-tel" maxLength={400} />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Product*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="Product">
|
||||
<select name="Product" required className="wpcf7-form-control wpcf7-select wpcf7-validates-as-required">
|
||||
<option value="">—Please choose an option—</option>
|
||||
{PRODUCT_OPTIONS.map((o) => (
|
||||
<option key={o} value={o}>{o}</option>
|
||||
))}
|
||||
</select>
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Interest*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="Interest">
|
||||
<select name="Interest" required className="wpcf7-form-control wpcf7-select wpcf7-validates-as-required">
|
||||
<option value="">—Please choose an option—</option>
|
||||
{INTEREST_OPTIONS.map((o) => (
|
||||
<option key={o} value={o}>{o}</option>
|
||||
))}
|
||||
</select>
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Annual Demand*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="AnnualDemand">
|
||||
<select name="AnnualDemand" required className="wpcf7-form-control wpcf7-select wpcf7-validates-as-required">
|
||||
<option value="">—Please choose an option—</option>
|
||||
{DEMAND_OPTIONS.map((o) => (
|
||||
<option key={o} value={o}>{o}</option>
|
||||
))}
|
||||
</select>
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Message*
|
||||
<br />
|
||||
<span className="wpcf7-form-control-wrap" data-name="your-message">
|
||||
<textarea name="your-message" required className="wpcf7-form-control wpcf7-textarea" rows={6} maxLength={2000} placeholder="Leave us a message about your project or product needs" />
|
||||
</span>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<span className="wpcf7-form-control-wrap" data-name="acceptance-918">
|
||||
<span className="wpcf7-list-item">
|
||||
<label>
|
||||
<input type="checkbox" name="acceptance-918" value="1" defaultChecked />
|
||||
<span className="wpcf7-list-item-label">
|
||||
By checking this box, you consent to receive commercial messages such as newsletters, event invitations, promotional and educational content,
|
||||
product updates, transaction-related emails, and customer service emails, from DusunIoT, in accordance with our{" "}
|
||||
<a href="/privacy-policy/">Privacy Policy</a>. You may withdraw your consent at any time.
|
||||
</span>
|
||||
</label>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<p className="ds-recaptcha-note">
|
||||
This site is protected by reCAPTCHA and the Google <a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Privacy Policy</a> and{" "}
|
||||
<a href="https://policies.google.com/terms" target="_blank" rel="noopener noreferrer">Terms of Service</a> apply.
|
||||
</p>
|
||||
<p>
|
||||
<input className="wpcf7-form-control wpcf7-submit" type="submit" value={status === "submitting" ? "Submitting…" : "Submit"} disabled={status === "submitting"} />
|
||||
</p>
|
||||
<div className={"wpcf7-response-output" + (status === "success" ? " ds-ok" : status === "error" ? " ds-err" : "")} aria-hidden={status === "idle"}>
|
||||
{status === "success" ? "Thank you for your message. It has been sent." : status === "error" ? errorMsg : ""}
|
||||
</div>
|
||||
</form>
|
||||
<Cf7Form604 formKey="contact-form-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+8
-2
@@ -33,8 +33,14 @@ async function build(): Promise<BunSQLDatabase<typeof schema>> {
|
||||
}
|
||||
if (!SQL) throw new Error("Bun SQL 不可用:请使用 bun 运行时启动服务");
|
||||
const { drizzle } = await import("drizzle-orm/bun-sql");
|
||||
const sql = new SQL(process.env.DATABASE_URL || "postgres://dusuniot:dusuniot@127.0.0.1:25432/dusuniot");
|
||||
return drizzle(sql, { schema }) as BunSQLDatabase<typeof schema>;
|
||||
// 连接池挂到 globalThis:Vite dev 每次 HMR 都会重新执行本模块,模块级缓存
|
||||
// 会随重载泄漏连接(实测热改数轮后 290 条 ESTABLISHED 触发 too many clients),
|
||||
// 全局只保留一个池,重载后的新模块实例复用它
|
||||
const gStore = g as typeof g & { __dusuniotSql?: import("bun").SQL };
|
||||
if (!gStore.__dusuniotSql) {
|
||||
gStore.__dusuniotSql = new SQL(process.env.DATABASE_URL || "postgres://dusuniot:dusuniot@127.0.0.1:25432/dusuniot");
|
||||
}
|
||||
return drizzle(gStore.__dusuniotSql, { schema }) as BunSQLDatabase<typeof schema>;
|
||||
}
|
||||
|
||||
/** # 函数 初始化数据库连接(幂等;仅服务端生效,浏览器端空操作)
|
||||
|
||||
+36
-41
@@ -27,56 +27,51 @@ export const Route = createFileRoute("/contact-us")({
|
||||
component: ContactPage,
|
||||
});
|
||||
|
||||
/** # 组件 联系页 */
|
||||
/** # 组件 联系页(原站为 full-canvas Elementor 模板:hero 通栏 + 表单卡叠压,不加 site-container) */
|
||||
function ContactPage() {
|
||||
const data = Route.useLoaderData();
|
||||
const elementor = extractElementor(data.meta);
|
||||
return (
|
||||
<Layout layout={data.layout}>
|
||||
<div className="site-container">
|
||||
{elementor ? (
|
||||
<ElementorRenderer doc={elementor} />
|
||||
) : (
|
||||
<>
|
||||
<Breadcrumb className="pt-6" aria-label="Breadcrumb">
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem><BreadcrumbLink to="/">Home</BreadcrumbLink></BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem><BreadcrumbPage>Contact Us</BreadcrumbPage></BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<h1 className="mt-0 text-[28px]">Contact Us</h1>
|
||||
<div className="mb-8 grid grid-cols-1 gap-[18px] min-[800px]:grid-cols-3">
|
||||
<div className="rounded-lg border border-line bg-soft p-5">
|
||||
<h3 className="mt-0">Sales & Inquiries</h3>
|
||||
<p>
|
||||
Email: <a href="mailto:sales@dusuniot.com">sales@dusuniot.com</a>
|
||||
<br />
|
||||
Tel: +86 755 2643 0850
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-line bg-soft p-5">
|
||||
<h3 className="mt-0">Visit Us</h3>
|
||||
<p>
|
||||
5F, Building B, Henglong Industrial Park,
|
||||
<br />
|
||||
Xili, Nanshan District, Shenzhen, Guangdong, China
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-line bg-soft p-5">
|
||||
<h3 className="mt-0">Support</h3>
|
||||
<p>24/7 technical support for OEM/ODM partners. Response within 24 hours on business days.</p>
|
||||
</div>
|
||||
{elementor ? (
|
||||
<ElementorRenderer doc={elementor} />
|
||||
) : (
|
||||
<div className="site-container">
|
||||
<Breadcrumb className="pt-6" aria-label="Breadcrumb">
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem><BreadcrumbLink to="/">Home</BreadcrumbLink></BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem><BreadcrumbPage>Contact Us</BreadcrumbPage></BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<h1 className="mt-0 text-[28px]">Contact Us</h1>
|
||||
<div className="mb-8 grid grid-cols-1 gap-[18px] min-[800px]:grid-cols-3">
|
||||
<div className="rounded-lg border border-line bg-soft p-5">
|
||||
<h3 className="mt-0">Sales & Inquiries</h3>
|
||||
<p>
|
||||
Email: <a href="mailto:sales@dusuniot.com">sales@dusuniot.com</a>
|
||||
<br />
|
||||
Tel: +86 755 2643 0850
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{/* Elementor 版页面(hero 内已含询盘表单)不再追加表单;标准布局才补一个询盘表单 */}
|
||||
{!elementor ? (
|
||||
<div className="rounded-lg border border-line bg-soft p-5">
|
||||
<h3 className="mt-0">Visit Us</h3>
|
||||
<p>
|
||||
5F, Building B, Henglong Industrial Park,
|
||||
<br />
|
||||
Xili, Nanshan District, Shenzhen, Guangdong, China
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-line bg-soft p-5">
|
||||
<h3 className="mt-0">Support</h3>
|
||||
<p>24/7 technical support for OEM/ODM partners. Response within 24 hours on business days.</p>
|
||||
</div>
|
||||
</div>
|
||||
<section className="mt-4 mb-14">
|
||||
<InquiryForm formKey="contact-us-form" title="Get a Quote" />
|
||||
</section>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,9 @@
|
||||
.fa-chevron-up::before { content: "\f077"; }
|
||||
.fa-comment-dots::before { content: "\f4ad"; }
|
||||
.fa-check-circle::before { content: "\f058"; }
|
||||
/* 联系页地址列(icon-list 38bf7c7/cbf19d1 等):信封与地图钉(FA Solid 码位) */
|
||||
.fa-envelope::before { content: "\f0e0"; }
|
||||
.fa-map-marker-alt::before { content: "\f3c5"; }
|
||||
|
||||
/* # 成员变量 Tailwind 主题令牌(与原 website.css 配色一一对应) **/
|
||||
@theme {
|
||||
|
||||
Reference in New Issue
Block a user