diff --git a/docs/sections-inventory.md b/docs/sections-inventory.md index 71919ee4a..c33d30f0d 100644 --- a/docs/sections-inventory.md +++ b/docs/sections-inventory.md @@ -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:登录注册表单 | | diff --git a/package.json b/package.json index 2dad7c4fb..1c08a09e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dusuniot-rebuild", - "version": "0.10.23", + "version": "0.10.24", "private": true, "type": "module", "scripts": { diff --git a/src/components/elementor/Renderer.tsx b/src/components/elementor/Renderer.tsx index d483a7c78..8915f75c7 100644 --- a/src/components/elementor/Renderer.tsx +++ b/src/components/elementor/Renderer.tsx @@ -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 ; case "eael-contact-form-7": - return ; + return ; case "video": return ; case "counter": @@ -1133,11 +1153,49 @@ function SocialIconsWidget({ settings }: { settings: Record }) } -/** # 组件 eael-contact-form-7(EAEL Contact Form 7 皮肤):渲染内置询盘表单 */ -function ContactFormWidget({ settings }: { settings: Record }) { +/** # 组件 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 设置生成节点级 +
+
+ +
+
); } diff --git a/src/components/elementor/slim.ts b/src/components/elementor/slim.ts index b54f803fa..ce98f17fe 100644 --- a/src/components/elementor/slim.ts +++ b/src/components/elementor/slim.ts @@ -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", diff --git a/src/components/website/Cf7Form604.tsx b/src/components/website/Cf7Form604.tsx new file mode 100644 index 000000000..d1091f2aa --- /dev/null +++ b/src/components/website/Cf7Form604.tsx @@ -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) { + 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 ( +
+ {/* 蜜罐字段:隐藏,机器人填写即视为垃圾提交(原站无此字段) */} + +

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+