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:
@@ -41,8 +41,8 @@
|
|||||||
| H5 | 可定制/易生产 双主张条 | heading ×2(双栏大字) | ✅ v0.10.7(long-arrow 背景条、列 380、文字 20px/500/#fff 单行、arrow 194×39) |
|
| H5 | 可定制/易生产 双主张条 | heading ×2(双栏大字) | ✅ v0.10.7(long-arrow 背景条、列 380、文字 20px/500/#fff 单行、arrow 194×39) |
|
||||||
| H6 | 从概念到量产 宣言条 | 全大写 heading(ACCELERATE YOUR IOT DEVICE LAUNCH…) | ✅ v0.10.8(Poppins 24px/600/行高 1 由 DB 内联、≤767 20px、列 59bc2e1 四向 10px、sec 根 padding 10px,五层盒模型 y 全对齐 0.8px 页级漂移内) |
|
| H6 | 从概念到量产 宣言条 | 全大写 heading(ACCELERATE YOUR IOT DEVICE LAUNCH…) | ✅ v0.10.8(Poppins 24px/600/行高 1 由 DB 内联、≤767 20px、列 59bc2e1 四向 10px、sec 根 padding 10px,五层盒模型 y 全对齐 0.8px 页级漂移内) |
|
||||||
| H7 | OS/芯片平台/语言/协议 四列 | heading ×4 图标列 | ✅ v0.10.8(外卡 66de90a 渐变+边框+圆角 8/16/16/0、四列 335.5 虚线分隔、_element_width:auto 图标横排 4+3 换行、el-w-auto 双层类修复 width:100% 强推回归) |
|
| H7 | OS/芯片平台/语言/协议 四列 | heading ×4 图标列 | ✅ v0.10.8(外卡 66de90a 渐变+边框+圆角 8/16/16/0、四列 335.5 虚线分隔、_element_width:auto 图标横排 4+3 换行、el-w-auto 双层类修复 width:100% 强推回归) |
|
||||||
| H8 | 热门嵌入式硬件 标题 | heading | ⬜ |
|
| H8 | 热门嵌入式硬件 标题 | heading(Popular Embedded Hardware Products…) | ✅ v0.10.9(heading 40px/600/#000/行高 1 居中 x38/w1360;section 7df0244 gap=no→列 0px、margin-top 4%=57.4375px、bg #F4F5F8、padding 64px 0——section 级 margin/gap 渲染支持随此版块落地,slim 白名单补 gap) |
|
||||||
| H9 | 热门硬件切换器(html) | html widget(原站 JS 切换逻辑,需看原站行为) | ⬜ |
|
| H9 | 热门硬件切换器(tabs) | Elementor 原生 tabs widget ×6 标签(内容为自包含 HTML 卡片网格) | ✅ v0.10.9(TabsWidget 忠实 DOM+React 点击切换/aria;widget-tabs.min.css 基础块+fd97340 生成规则(border_width=0 含伪元素)+custom_css(.ele-soms-chip-dif 边框/内容覆盖)三层取证;全局修正 body 行高为无单位 1.5、补 kit 链接字体 Roboto(卡片 <a> 经 .elementor-kit-15 a 规则用 Roboto,否则 Arial 截断致规格多折一行);实测仅 0.8px 页级漂移) |
|
||||||
| H10 | 技术产品 Tab 行 ×3 | heading+image+image-box ×4+button(三段 Tab 面板) | ⬜ |
|
| H10 | 技术产品 Tab 行 ×3 | heading+image+image-box ×4+button(三段 Tab 面板) | ⬜ |
|
||||||
| H11 | Industries & Use Cases | heading + eael-adv-tabs + button(多标签竖排) | ⬜ |
|
| H11 | Industries & Use Cases | heading + eael-adv-tabs + button(多标签竖排) | ⬜ |
|
||||||
| H12 | The Difference Maker 段一 | heading+text+icon-list+text+button(左侧列表优势) | ⬜ |
|
| H12 | The Difference Maker 段一 | heading+text+icon-list+text+button(左侧列表优势) | ⬜ |
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dusuniot-rebuild",
|
"name": "dusuniot-rebuild",
|
||||||
"version": "0.10.8",
|
"version": "0.10.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* 样式以内联 style 复现视觉效果(背景、内边距、对齐、列宽百分比),
|
* 样式以内联 style 复现视觉效果(背景、内边距、对齐、列宽百分比),
|
||||||
* 未识别的 widgetType 优雅降级:渲染 heading_title / editor 文本或忽略。
|
* 未识别的 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 { getLatestPosts, getLatestProducts, type LatestItem } from "~/server/data";
|
||||||
import TickerBar from "~/components/website/TickerBar";
|
import TickerBar from "~/components/website/TickerBar";
|
||||||
import { InquiryForm } from "~/components/website/InquiryForm";
|
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 width = sizeCss(settings.content_width) || sizeCss((settings as Record<string, unknown>).width) || "1140px";
|
||||||
const secPad = paddingCss(settings.padding);
|
const secPad = paddingCss(settings.padding);
|
||||||
if (secPad) style.padding = secPad;
|
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 = {};
|
const inner: CSSProperties = {};
|
||||||
if (s(settings.layout) === "full_width") {
|
if (s(settings.layout) === "full_width") {
|
||||||
// 通栏 section:内容不设上限(如首页 hero 轮播)
|
// 通栏 section:内容不设上限(如首页 hero 轮播)
|
||||||
@@ -1091,24 +1094,73 @@ function AccordionWidget({ settings }: { settings: Record<string, unknown> }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** # 组件 tabs widget:标签页 */
|
/** # 组件 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> }) {
|
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;
|
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 (
|
return (
|
||||||
<Tabs defaultValue="tab-0" className="mb-[18px]">
|
<div className={`elementor-widget-tabs elementor-tabs elementor-tabs-view-horizontal${alignCls}`}>
|
||||||
<TabsList>
|
<div className="elementor-tabs-wrapper" role="tablist">
|
||||||
{tabs.map((t, i) => (
|
{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)}
|
{s(t.tab_title)}
|
||||||
</TabsTrigger>
|
</div>
|
||||||
))}
|
))}
|
||||||
</TabsList>
|
</div>
|
||||||
{tabs.map((t, i) => (
|
<div className="elementor-tabs-content-wrapper" role="tablist">
|
||||||
<TabsContent key={i} value={`tab-${i}`}>
|
{tabs.map((t, i) => (
|
||||||
<div className="content-html" dangerouslySetInnerHTML={{ __html: s(t.tab_content) }} />
|
<Fragment key={t._id || i}>
|
||||||
</TabsContent>
|
<div
|
||||||
))}
|
className={"elementor-tab-title elementor-tab-mobile-title" + (i === active ? " elementor-active" : "")}
|
||||||
</Tabs>
|
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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ const SETTING_KEYS = new Set([
|
|||||||
// 列结构
|
// 列结构
|
||||||
"_column_size",
|
"_column_size",
|
||||||
"_inline_size",
|
"_inline_size",
|
||||||
|
// section 间隙档位(决定子列默认 populated padding:no 0 / default 10 / …)
|
||||||
|
"gap",
|
||||||
// 各 widget 数据
|
// 各 widget 数据
|
||||||
"heading_title",
|
"heading_title",
|
||||||
"title",
|
"title",
|
||||||
@@ -79,6 +81,8 @@ const SETTING_KEYS = new Set([
|
|||||||
"slides",
|
"slides",
|
||||||
"slides_height",
|
"slides_height",
|
||||||
"tabs",
|
"tabs",
|
||||||
|
"tabs_align_horizontal",
|
||||||
|
"tab_active_color",
|
||||||
"video_url",
|
"video_url",
|
||||||
"youtube_url",
|
"youtube_url",
|
||||||
"map",
|
"map",
|
||||||
|
|||||||
+76
-3
@@ -64,10 +64,11 @@
|
|||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
html, body { margin: 0; padding: 0; }
|
html, body { margin: 0; padding: 0; }
|
||||||
body {
|
body {
|
||||||
/* 基准取原站实测(2026-10-31 headless):16px / #333 / 行高 24px(hello-elementor 默认) */
|
/* 基准取原站实测(2026-10-31 headless):16px / #333;行高为无单位 1.5
|
||||||
|
* (原站 action 按钮 fs14 → 21px 随字号缩放,绝对 24px 会固定不变) */
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 1.5;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
@@ -296,6 +297,12 @@
|
|||||||
/* ===== Elementor hover 动画(e-animation-shrink.min.css):hover 缩至 90%,0.3s 过渡 */
|
/* ===== Elementor hover 动画(e-animation-shrink.min.css):hover 缩至 90%,0.3s 过渡 */
|
||||||
/* heading 标题链接颜色继承(原站不受全局品牌橙影响,实测 #333/#000) */
|
/* heading 标题链接颜色继承(原站不受全局品牌橙影响,实测 #333/#000) */
|
||||||
.el-heading-title a { color: inherit; }
|
.el-heading-title a { color: inherit; }
|
||||||
|
/* kit 全局链接排版(post-15.css .elementor-kit-15 a):链接默认 Roboto 400。
|
||||||
|
* 取证:tab 卡片 .card 为 <a>,kit 字体使其内部 ul/li 用 Roboto 渲染(文字 273.6px),
|
||||||
|
* 否则被卡片内嵌样式的 Arial 截住(261.9px),第 4 条规格多折一行(H9 body 高 +21.6)。
|
||||||
|
* 颜色不在此外加:各 widget 链接色已由自身规则(.el-heading-title a 等)与原站一致。
|
||||||
|
*/
|
||||||
|
.content-html a { font-family: "Roboto", sans-serif; font-weight: 400; }
|
||||||
.el-anim-shrink { transition-duration: 0.3s; transition-property: transform; }
|
.el-anim-shrink { transition-duration: 0.3s; transition-property: transform; }
|
||||||
.el-anim-shrink:hover, .el-anim-shrink:focus, .el-anim-shrink:active { transform: scale(0.9); }
|
.el-anim-shrink:hover, .el-anim-shrink:focus, .el-anim-shrink:active { transform: scale(0.9); }
|
||||||
|
|
||||||
@@ -404,8 +411,74 @@
|
|||||||
border-right: 0;
|
border-right: 0;
|
||||||
border-bottom: 1px dashed #E0E5F9;
|
border-bottom: 1px dashed #E0E5F9;
|
||||||
}
|
}
|
||||||
[data-el="338fb86"] { padding-inline: 1rem; }
|
/* 338fb86 移动端 padding 由 padding_mobile(collectResponsiveCss)在根上生效 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== Elementor 原生 tabs widget(widget-tabs.min.css 对应块;H9 热门硬件切换器)
|
||||||
|
* 原站为水平视图 + alignment-center;竖排/其余档位规则随源码保留(无实例不生效)。
|
||||||
|
* 源文件的 border:1px #d5d8dc 简写缺 style,按规范整条无效——与原站一致不产生边框,未移植。
|
||||||
|
* active 下边框 4px #ed6c00 与内容 wrapper 顶部 1px #C7CBDA 由节点 custom_css
|
||||||
|
* (.ele-soms-chip-dif …,collectCustomCss 注入)提供。
|
||||||
|
*/
|
||||||
|
.elementor-widget-tabs .elementor-tabs { text-align: start; }
|
||||||
|
/* 源文件的 border:1px #d5d8dc 简写(实测原站伪元素 color=#d5d8dc 生效)——按可解析移植,
|
||||||
|
* 仅取颜色;宽度随后被 fd97340 的生成规则(border_width=0, 含伪元素)归零 */
|
||||||
|
.elementor-widget-tabs .elementor-tab-content,
|
||||||
|
.elementor-widget-tabs .elementor-tab-title,
|
||||||
|
.elementor-widget-tabs .elementor-tab-title:after,
|
||||||
|
.elementor-widget-tabs .elementor-tab-title:before,
|
||||||
|
.elementor-widget-tabs .elementor-tabs-content-wrapper { border-color: #d5d8dc; }
|
||||||
|
.elementor-widget-tabs .elementor-tabs-wrapper { overflow: hidden; }
|
||||||
|
.elementor-widget-tabs .elementor-tab-title { cursor: pointer; outline: var(--focus-outline, none); }
|
||||||
|
.elementor-widget-tabs .elementor-tab-desktop-title {
|
||||||
|
border: solid transparent;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 20px 25px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.elementor-widget-tabs .elementor-tab-desktop-title.elementor-active { border-color: #d5d8dc; }
|
||||||
|
.elementor-widget-tabs .elementor-tab-desktop-title.elementor-active:after,
|
||||||
|
.elementor-widget-tabs .elementor-tab-desktop-title.elementor-active:before {
|
||||||
|
content: ""; display: block; position: absolute;
|
||||||
|
}
|
||||||
|
.elementor-widget-tabs .elementor-tab-desktop-title:focus-visible { border: 1px solid #000; }
|
||||||
|
.elementor-widget-tabs .elementor-tab-mobile-title { cursor: pointer; padding: 10px; }
|
||||||
|
.elementor-widget-tabs .elementor-tab-content { display: none; padding: 20px; }
|
||||||
|
.elementor-widget-tabs.elementor-tabs-view-horizontal .elementor-tab-desktop-title { display: table-cell; }
|
||||||
|
.elementor-widget-tabs.elementor-tabs-view-horizontal .elementor-tab-desktop-title.elementor-active { border-block-end-style: none; }
|
||||||
|
.elementor-widget-tabs.elementor-tabs-view-horizontal .elementor-tab-desktop-title.elementor-active:after,
|
||||||
|
.elementor-widget-tabs.elementor-tabs-view-horizontal .elementor-tab-desktop-title.elementor-active:before {
|
||||||
|
border-block-end-style: solid; height: 0; inset-block-end: 0; width: 999em;
|
||||||
|
}
|
||||||
|
.elementor-widget-tabs.elementor-tabs-view-horizontal .elementor-tab-desktop-title.elementor-active:before { inset-inline-end: 100%; }
|
||||||
|
.elementor-widget-tabs.elementor-tabs-view-horizontal .elementor-tab-desktop-title.elementor-active:after { inset-inline-start: 100%; }
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.elementor-widget-tabs.elementor-tabs-alignment-center .elementor-tabs-wrapper,
|
||||||
|
.elementor-widget-tabs.elementor-tabs-alignment-end .elementor-tabs-wrapper,
|
||||||
|
.elementor-widget-tabs.elementor-tabs-alignment-stretch .elementor-tabs-wrapper { display: flex; }
|
||||||
|
.elementor-widget-tabs.elementor-tabs-alignment-center .elementor-tabs-wrapper { justify-content: center; }
|
||||||
|
.elementor-widget-tabs.elementor-tabs-alignment-end .elementor-tabs-wrapper { justify-content: flex-end; }
|
||||||
|
.elementor-widget-tabs.elementor-tabs-alignment-stretch.elementor-tabs-view-horizontal .elementor-tab-title { width: 100%; }
|
||||||
|
.elementor-tabs .elementor-tab-mobile-title { display: none; }
|
||||||
|
.elementor-widget-tabs.elementor-tabs-view-horizontal .elementor-tab-content { border-style: solid; border-block-start-style: none; }
|
||||||
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.elementor-tabs .elementor-tab-content, .elementor-tabs .elementor-tab-title { border-style: solid; border-block-end-style: none; }
|
||||||
|
.elementor-tabs .elementor-tabs-wrapper { display: none; }
|
||||||
|
.elementor-tabs .elementor-tabs-content-wrapper { border-block-end-style: solid; }
|
||||||
|
.elementor-tabs .elementor-tab-content { padding: 10px; }
|
||||||
|
}
|
||||||
|
.elementor-clearfix:after { clear: both; content: ""; display: block; height: 0; width: 0; }
|
||||||
|
/* fd97340 生成规则:border_width=0 → 标题/伪元素/内容面板 border-width 0(原站实测 content 无边框、
|
||||||
|
* active 伪元素 0px solid #d5d8dc,而移植的 view-horizontal 规则给了 border-style:solid,
|
||||||
|
* 宽度须归零,否则 medium 3px 挤窄内容 6px / 画出通栏橙线);
|
||||||
|
* active 下边框 4px 与颜色由 custom_css !important / 生成规则覆盖 */
|
||||||
|
[data-el="fd97340"] .elementor-tab-title,
|
||||||
|
[data-el="fd97340"] .elementor-tab-title:before,
|
||||||
|
[data-el="fd97340"] .elementor-tab-title:after,
|
||||||
|
[data-el="fd97340"] .elementor-tab-content { border-width: 0; }
|
||||||
|
[data-el="fd97340"] .elementor-tab-title.elementor-active { color: #ED6C00; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
|
|||||||
Reference in New Issue
Block a user