diff --git a/docs/sections-inventory.md b/docs/sections-inventory.md index da74662ed..516eb3545 100644 --- a/docs/sections-inventory.md +++ b/docs/sections-inventory.md @@ -36,7 +36,7 @@ | H0 | Banner 轮播 | slides ×4(Swiper,fadeInUp 内容、5000ms 自动播) | ✅ v0.10.3 | | H1 | 公告滚动条 | eael-content-ticker | ✅ v0.10.4(badge 18px/600、内容 #222 hover #ED6C00、fade 200ms/5000ms、1300px 容器、bg #F4F5F8,数据取最新 3 产品) | | H2 | 产品线入口 ×6 | image-box ×6(6 条产品线图标卡) | ✅ v0.10.5(图片上方图标+标题 16px/400/#333、figure 30% 宽、hover 亮度 90%、五列右侧 v-line 竖线、列 10px 内边距、≤767 50% 两列;ImageBoxWidget 重构为 Elementor 忠实 DOM 供 H13/about 复用) | -| H3 | 「Dusun Hardware & Service」标题条 | heading | ⬜ | +| H3 | 「Dusun Hardware & Service」标题条 | heading | ✅ v0.10.6(Poppins®600/40px/48 行高/#000 居中、widget 底 padding 24px、section 上 padding 64px;Poppins 与原站一致走系统 fallback 不载字体文件;typographyCss/_padding/resolveGlobals 修复基建) | | H4 | 硬件与服务系列 ×5 组 | 每组 heading+text+image+eael-info-box ×4(横向四列) | ⬜ | | H5 | 可定制/易生产 双主张条 | heading ×2(双栏大字) | ⬜ | | H6 | 从概念到量产 宣言条 | 全大写 heading(ACCELERATE YOUR IOT DEVICE LAUNCH…) | ⬜ | diff --git a/package.json b/package.json index 6114384b0..e7fdb0436 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dusuniot-rebuild", - "version": "0.10.5", + "version": "0.10.6", "private": true, "type": "module", "scripts": { diff --git a/src/components/elementor/Renderer.tsx b/src/components/elementor/Renderer.tsx index 9afa90b1a..96ad98ff1 100644 --- a/src/components/elementor/Renderer.tsx +++ b/src/components/elementor/Renderer.tsx @@ -72,6 +72,37 @@ function collapseBoxValues(v: string): string { return v; } +/** # 函数 解析 Typography 组合样式(typography_typography=custom 时的 typography_* 字段) + * 覆盖 font-family/size/weight/line-height/letter-spacing/word-spacing/text-transform/ + * font-style/text-decoration(凡 settings 中有定义的值写入内联样式), + * 供 heading / text-editor / image-box 等按原站 kit 逐字重渲染。 + * 参数 settings 元素 settings;prefix 通常为 "typography" + * 返回值 CSSProperties(无 custom 时为空对象) + */ +function typographyCss(settings: Record, prefix = "typography"): CSSProperties { + const style: CSSProperties = {}; + if (s(settings[`${prefix}_typography`]) !== "custom") return style; + const fam = s(settings[`${prefix}_font_family`]); + if (fam) style.fontFamily = `"${fam}", sans-serif`; + const size = sizeCss(settings[`${prefix}_font_size`]); + if (size) style.fontSize = size; + const weight = s(settings[`${prefix}_font_weight`]); + if (weight) style.fontWeight = weight as CSSProperties["fontWeight"]; + const lh = sizeCss(settings[`${prefix}_line_height`]); + if (lh) style.lineHeight = lh; + const ls = sizeCss(settings[`${prefix}_letter_spacing`]); + if (ls) style.letterSpacing = ls; + const ws = sizeCss(settings[`${prefix}_word_spacing`]); + if (ws) style.wordSpacing = ws; + const tt = s(settings[`${prefix}_text_transform`]); + if (tt) style.textTransform = tt as CSSProperties["textTransform"]; + const fs = s(settings[`${prefix}_font_style`]); + if (fs) style.fontStyle = fs as CSSProperties["fontStyle"]; + const td = s(settings[`${prefix}_text_decoration`]); + if (td) style.textDecoration = td as CSSProperties["textDecoration"]; + return style; +} + /** # 函数 解析 padding(Elementor 可能是对象/字符串/数组) */ function paddingCss(v: unknown): string | undefined { if (!v) return undefined; @@ -132,9 +163,9 @@ function resolveGlobals(settings: T): T { return settings; } -/** # 函数 单值全局色引用解析 */ +/** # 函数 单值全局色引用解析(键名含非十六进制 id:text/primary/accent 等) */ function resolveGlobalsStr(v: string): string { - const m = /^globals\/colors\?id=([0-9a-f]+)$/.exec(v.trim()); + const m = /^globals\/colors\?id=([\w-]+)$/.exec(v.trim()); if (m) return ELEMENTOR_GLOBAL_COLORS[m[1]!] ?? v; return v; } @@ -160,8 +191,7 @@ function sectionBgStyle(settings: Record): CSSProperties { } else if (s(settings.background_color)) { style.backgroundColor = s(settings.background_color); } - const pad = paddingCss(settings.padding); - if (pad) style.padding = pad; + // padding 由调用方(SectionNode 写到内容容器 / ColumnNode 写到列)负责,不在此叠加避免双份 return style; } @@ -436,13 +466,19 @@ function WidgetNodeInner({ el }: { el: ElElement }) { /** # 组件 heading widget:标题级别/颜色/对齐 */ function HeadingWidget({ settings }: { settings: Record }) { + // 标签合法域同原站 Utils::validate_html_tag(h1..h6 / div / span / p);默认 h2 const size = s(settings.header_size) || "h2"; - const Tag = (["h1", "h2", "h3", "h4", "h5", "h6"].includes(size) ? size : "h2") as "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; + const Tag = (["h1", "h2", "h3", "h4", "h5", "h6", "div", "span", "p"].includes(size) ? size : "h2") as "h2"; const style: CSSProperties = {}; if (s(settings.title_color)) style.color = s(settings.title_color); const align = s(settings.align); if (align) style.textAlign = align as CSSProperties["textAlign"]; - return ; + Object.assign(style, typographyCss(settings)); + // widget 自带内边距(Elementor 高级项 _padding) + const pad = paddingCss(settings._padding); + if (pad) style.padding = pad; + // 通用 margin 不设(实测原站 heading widget margin 0,间距全部来自原站 settings) + return ; } /** # 组件 text-editor widget:富文本 HTML */ @@ -451,7 +487,12 @@ function TextEditorWidget({ settings }: { settings: Record }) { if (!html) return null; // 文本颜色可能来自全局色(如蓝底白字副标题),style 方式应用 const color = s(settings.text_color); - return
; + const style: CSSProperties = {}; + if (color) style.color = color; + Object.assign(style, typographyCss(settings)); + const pad = paddingCss(settings._padding); + if (pad) style.padding = pad; + return
; } /** # 组件 image widget */ diff --git a/src/components/elementor/slim.ts b/src/components/elementor/slim.ts index 485e03785..6748b1422 100644 --- a/src/components/elementor/slim.ts +++ b/src/components/elementor/slim.ts @@ -37,6 +37,22 @@ const SETTING_KEYS = new Set([ "color_a", "color_b", "header_size", + // 组件自带盒间距与列 margin(heading/text-editor 的 _padding;内层列 margin) + "_padding", + "margin", + // Typography 组合字段(typography_typography=custom 时逐键采用) + "typography_typography", + "typography_font_family", + "typography_font_size", + "typography_font_size_tablet", + "typography_font_size_mobile", + "typography_font_weight", + "typography_line_height", + "typography_letter_spacing", + "typography_word_spacing", + "typography_text_transform", + "typography_font_style", + "typography_text_decoration", // 列结构 "_column_size", "_inline_size", diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index c00e483a9..7d6792938 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -28,5 +28,8 @@ function RootDocument() { ); } -/** # 成员变量 根路由实例 **/ +/** # 成员变量 根路由实例 + * (取证:原站 head 无 Poppins css link、无 woff2 请求,Poppins 仅作 CSS 声明, + * 实际渲染走系统 fallback;本地同样不加载字体文件,保持两边一致回退。) + **/ export const Route = createRootRoute({ component: RootDocument, loader });