v0.10.9 首页 H8 标题条 + H9 tabs 切换器 1:1:TabsWidget 忠实 DOM/React 切换;widget-tabs 基础块+生成规则+custom_css 三层;section margin/gap 渲染支持;全局 body 行高改无单位 1.5、kit 链接字体 Roboto;H1~H7 全量回归无差异

This commit is contained in:
bigcode
2026-11-01 10:34:54 +08:00
parent 2cbfc82a08
commit f66f4775d6
5 changed files with 148 additions and 19 deletions
+65 -13
View File
@@ -5,7 +5,7 @@
* 样式以内联 style 复现视觉效果(背景、内边距、对齐、列宽百分比),
* 未识别的 widgetType 优雅降级:渲染 heading_title / editor 文本或忽略。
*/
import { Component, useEffect, useState, type CSSProperties, type ReactNode } from "react";
import { Component, Fragment, useEffect, useState, type CSSProperties, type ReactNode } from "react";
import { getLatestPosts, getLatestProducts, type LatestItem } from "~/server/data";
import TickerBar from "~/components/website/TickerBar";
import { InquiryForm } from "~/components/website/InquiryForm";
@@ -320,6 +320,9 @@ function SectionNode({ el }: { el: ElElement }) {
const width = sizeCss(settings.content_width) || sizeCss((settings as Record<string, unknown>).width) || "1140px";
const secPad = paddingCss(settings.padding);
if (secPad) style.padding = secPad;
// section 外边距(原站 computed 在根元素,如 H8 7df0244 margin-top 4% = 57.4px @1436)
const secMargin = paddingCss(settings.margin);
if (secMargin) style.margin = secMargin;
const inner: CSSProperties = {};
if (s(settings.layout) === "full_width") {
// 通栏 section:内容不设上限(如首页 hero 轮播)
@@ -1091,24 +1094,73 @@ function AccordionWidget({ settings }: { settings: Record<string, unknown> }) {
}
/** # 组件 tabs widget:标签页 */
/** # 组件 Elementor 原生 tabs widget(H9 热门嵌入式硬件切换器)
* DOM 对齐原站(1436 实测 2026-11-01):.elementor-tabs > .elementor-tabs-wrapper(desktop 标题行,
* role=tablist,flex 居中) + .elementor-tabs-content-wrapper(mobile 标题 ×N + 内容面板 ×N)。
* active 用 elementor-active 类;点击切换(React state,原站为 Elementor JS 行为);
* 内容 tab_content 为自包含 HTML(含 <style> 卡片网格样式),dangerouslySetInnerHTML 原样输出。
* 基础样式移植 widget-tabs.min.css(app.css「Elementor tabs」块),本节点 custom_css
* (.ele-soms-chip-dif 边框/内容覆盖)经 collectCustomCss 自动注入——因此类名必须用
* elementor 原名,不得改 el- 前缀。id 基数 2651:原站渲染计数 elementor-tab-title-2651…,
* 本地按序复刻(仅影响锚点,无视觉作用)。
*/
function TabsWidget({ settings }: { settings: Record<string, unknown> }) {
const tabs = Array.isArray(settings.tabs) ? (settings.tabs as { tab_title?: string; tab_content?: string }[]) : [];
const tabs = Array.isArray(settings.tabs)
? (settings.tabs as { _id?: string; tab_title?: string; tab_content?: string }[])
: [];
const [active, setActive] = useState(0);
if (!tabs.length) return null;
const align = s(settings.tabs_align_horizontal) || "center";
const alignCls =
align === "center" ? " elementor-tabs-alignment-center"
: align === "end" ? " elementor-tabs-alignment-end"
: align === "stretch" ? " elementor-tabs-alignment-stretch"
: "";
const base = 2651;
return (
<Tabs defaultValue="tab-0" className="mb-[18px]">
<TabsList>
<div className={`elementor-widget-tabs elementor-tabs elementor-tabs-view-horizontal${alignCls}`}>
<div className="elementor-tabs-wrapper" role="tablist">
{tabs.map((t, i) => (
<TabsTrigger key={i} value={`tab-${i}`}>
<div
key={t._id || i}
id={`elementor-tab-title-${base + i}`}
className={"elementor-tab-title elementor-tab-desktop-title" + (i === active ? " elementor-active" : "")}
role="tab"
data-tab={i + 1}
tabIndex={i === active ? 0 : -1}
aria-selected={i === active}
onClick={() => setActive(i)}
>
{s(t.tab_title)}
</TabsTrigger>
</div>
))}
</TabsList>
{tabs.map((t, i) => (
<TabsContent key={i} value={`tab-${i}`}>
<div className="content-html" dangerouslySetInnerHTML={{ __html: s(t.tab_content) }} />
</TabsContent>
))}
</Tabs>
</div>
<div className="elementor-tabs-content-wrapper" role="tablist">
{tabs.map((t, i) => (
<Fragment key={t._id || i}>
<div
className={"elementor-tab-title elementor-tab-mobile-title" + (i === active ? " elementor-active" : "")}
role="tab"
data-tab={i + 1}
tabIndex={i === active ? 0 : -1}
aria-selected={i === active}
onClick={() => setActive(i)}
>
{s(t.tab_title)}
</div>
<div
id={`elementor-tab-content-${base + i}`}
className="elementor-tab-content elementor-clearfix"
role="tabpanel"
data-tab={i + 1}
tabIndex={0}
style={{ display: i === active ? "block" : "none" }}
dangerouslySetInnerHTML={{ __html: s(t.tab_content) }}
/>
</Fragment>
))}
</div>
</div>
);
}