v0.10.23: 落地页收口(toggle/内嵌模板 27143/posts cards/info-box)+ dev 3311 修复
This commit is contained in:
@@ -375,6 +375,14 @@ function collectResponsiveCss(elements: ElElement[]): string {
|
||||
const iw = sizeCss(st.width_mobile);
|
||||
if (iw) css += `@media (max-width:767px){${sel} img{width:${iw} !important;}}\n`;
|
||||
}
|
||||
// 标题响应式字号(typography_font_size_tablet/mobile;落地页 e8c891a
|
||||
// 桌面 38px 来自主题,移动端设置 1.5rem=24px,原站实测 24px/24)
|
||||
if (s(el.widgetType) === "heading") {
|
||||
const fsT = sizeCss(st.typography_font_size_tablet);
|
||||
const fsM = sizeCss(st.typography_font_size_mobile);
|
||||
if (fsT) css += `@media (max-width:1024px){${sel} .el-heading-title{font-size:${fsT} !important;}}\n`;
|
||||
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;
|
||||
@@ -731,6 +739,7 @@ function WidgetNodeInner({ el }: { el: ElElement }) {
|
||||
</div>
|
||||
);
|
||||
case "toggle":
|
||||
return <ToggleWidget el={el} settings={settings} />;
|
||||
case "accordion":
|
||||
return <AccordionWidget settings={settings} />;
|
||||
case "tabs":
|
||||
@@ -961,26 +970,45 @@ function TickerWidget({ settings }: { settings: Record<string, unknown> }) {
|
||||
}
|
||||
|
||||
/** # 组件 posts widget(Elementor Pro 卡片皮肤:文章/产品卡片网格) */
|
||||
/** # 组件 posts widget(Elementor skin cards 忠实 DOM,H18 实测 2026-11-01)
|
||||
/** # 组件 posts widget(Elementor skin cards 忠实 DOM)
|
||||
* DOM/样式对齐 widget-posts.min.css 的 cards 皮肤(原站 H18 与模板 27143 实测 2026-11-02):
|
||||
* .elementor-posts-container.elementor-posts.elementor-posts--skin-cards.elementor-grid
|
||||
* (grid 列数 cards_columns、gap 16/10=row/column)> article.elementor-post > .elementor-post__card
|
||||
* (flex 纵向、边框/底色/圆角 3 由设置)> [缩略图链接(比率 0.66 内宽、badge 绝对右上)]
|
||||
* + .elementor-post__text(padding 12) > .elementor-post__title(div,fs18/400,#000)
|
||||
* + .elementor-post__meta-data(日期 F j, Y,#ADADAD) + .elementor-post__excerpt(18 词,#767676,底距 44);
|
||||
* 缩略图 thumbnail=none 时省略(右列卡 74 高纯文本);文章数据经 PostsDataContext 预取。
|
||||
* > article.elementor-post > .elementor-post__card(纵向 flex、白底、1px 边、圆角 3)
|
||||
* > [a.__thumbnail__link > .__thumbnail(padding-bottom 比率) > img]
|
||||
* + .__badge(仅缩略图存在时,绝对右上)
|
||||
* + .__text(flex-grow)> .__title + .__excerpt
|
||||
* + .__meta-data(text 之后,border-top + margin-top:auto,日期 F j, Y)
|
||||
* 图片宽度 calc(100%+1px) 且为静态流(高度按原图比例,原站即如此);
|
||||
* 缩略图比率来自 cards_item_ratio(默认 0.66)/ cards_item_ratio_mobile。
|
||||
* 文章数据经 PostsDataContext 预取(server 端 loadPostsWidgetsData)。
|
||||
*/
|
||||
function PostsWidget({ el, settings }: { el: ElElement; settings: Record<string, unknown> }) {
|
||||
const allPosts = useContext(PostsDataContext);
|
||||
const posts = (el.id && allPosts[el.id]) || [];
|
||||
if (!posts.length) return null;
|
||||
const numVal = (v: unknown): number | undefined => {
|
||||
const n = typeof v === "number" ? v : Number(s(v));
|
||||
return Number.isFinite(n) && n > 0 ? n : undefined;
|
||||
};
|
||||
const showThumb = s(settings.cards_thumbnail) !== "none";
|
||||
const cols = Number(s(settings.cards_columns)) || 1;
|
||||
const cols = numVal(settings.cards_columns) ?? 1;
|
||||
const colsT = numVal(settings.cards_columns_tablet) ?? Math.min(cols, 2);
|
||||
const colsM = numVal(settings.cards_columns_mobile) ?? 1;
|
||||
const rowGap = sizeCss(settings.cards_row_gap) || "16px";
|
||||
const colGap = sizeCss(settings.cards_column_gap) || "10px";
|
||||
const textPad = sizeCss(settings.cards_card_padding) || "12px";
|
||||
const titleSpacing = sizeCss(settings.cards_title_spacing) || "16px";
|
||||
const excSpacing = sizeCss(settings.cards_excerpt_spacing) || "44px";
|
||||
const excerptColor = s(settings.cards_excerpt_color) || undefined;
|
||||
const excerptColor = s(settings.cards_excerpt_color) || "#777";
|
||||
const excerptSize = sizeCss(settings.cards_excerpt_typography_font_size) || "14px";
|
||||
const titleSize = sizeCss(settings.cards_title_typography_font_size) || "18px";
|
||||
const metaSize = sizeCss(settings.cards_meta_typography_font_size) || "12px";
|
||||
const metaWeight = s(settings.cards_meta_typography_font_weight) || "400";
|
||||
const cardBorderColor = s(settings.cards_card_border_color) || "#69727d";
|
||||
const cardBorderWidth = sizeCss(settings.cards_card_border_width) || "1px";
|
||||
// 缩略图比率:desktop 默认 0.66,mobile 默认继承(27143/ H18 均为 0.6/0.67)
|
||||
const ratio = numVal((settings.cards_item_ratio as { size?: unknown } | undefined)?.size) ?? 0.66;
|
||||
const ratioM = numVal((settings.cards_item_ratio_mobile as { size?: unknown } | undefined)?.size) ?? ratio;
|
||||
const titleTag = (["h1", "h2", "h3", "h4", "h5", "h6", "div", "span", "p"].includes(s(settings.cards_title_tag)) ? s(settings.cards_title_tag) : "h3") as "h3";
|
||||
const TitleTag = titleTag;
|
||||
// 徽章/日期/摘要开关:switcher 键缺省 = 开(左卡无键但有徽章/摘要的实测语义),
|
||||
@@ -995,44 +1023,57 @@ function PostsWidget({ el, settings }: { el: ElElement; settings: Record<string,
|
||||
if (isNaN(dt.getTime())) return null;
|
||||
return dt.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });
|
||||
};
|
||||
const thumbVars = { "--ep-ratio": String(ratio), "--ep-ratio-m": String(ratioM) } as CSSProperties;
|
||||
return (
|
||||
<div
|
||||
className="elementor-posts-container elementor-posts elementor-posts--skin-cards elementor-grid"
|
||||
style={{ display: "grid", gridTemplateColumns: `repeat(${cols}, 1fr)`, gap: `${rowGap} ${colGap}` }}
|
||||
className="elementor-posts-container elementor-posts elementor-posts--skin-cards elementor-posts--thumbnail-top elementor-posts__hover-gradient elementor-grid"
|
||||
style={{
|
||||
display: "grid",
|
||||
gap: `${rowGap} ${colGap}`,
|
||||
// 列数随断点(Elementor grid-4 / tablet-2 / mobile-1;值由 app.css 媒体查询读取)
|
||||
["--ep-cols" as string]: String(cols),
|
||||
["--ep-cols-t" as string]: String(colsT),
|
||||
["--ep-cols-m" as string]: String(colsM),
|
||||
}}
|
||||
>
|
||||
{posts.map((p) => {
|
||||
// 链接前缀按主分类:News → /news/,其余 → /blog/(原站 News 文章走 /news/<slug>/)
|
||||
const href = (p.categoryName === "News" ? "/news/" : "/blog/") + p.slug + "/";
|
||||
const hasThumb = showThumb && !!p.imageUrl;
|
||||
return (
|
||||
<article key={p.slug} className="elementor-post elementor-grid-item post type-post status-publish format-standard has-post-thumbnail">
|
||||
<div className="elementor-post__card">
|
||||
{showThumb && p.imageUrl ? (
|
||||
<div className="elementor-post__card" style={{ border: `${cardBorderWidth} solid ${cardBorderColor}` }}>
|
||||
{hasThumb ? (
|
||||
<a className="elementor-post__thumbnail__link" href={href} tabIndex={-1}>
|
||||
<div className="elementor-post__thumbnail">
|
||||
<img src={localSiteUrl(p.imageUrl)} alt={p.title} loading="lazy" />
|
||||
<div className="elementor-post__thumbnail" style={thumbVars}>
|
||||
<img src={localSiteUrl(p.imageUrl!)} alt={p.title} loading="lazy" />
|
||||
</div>
|
||||
{showBadge && p.categoryName ? (
|
||||
<span className="elementor-post__badge">{p.categoryName}</span>
|
||||
) : null}
|
||||
</a>
|
||||
) : null}
|
||||
<div className="elementor-post__text">
|
||||
<TitleTag className="elementor-post__title" style={{ marginBottom: titleSpacing }}>
|
||||
{hasThumb && showBadge && p.categoryName ? (
|
||||
<div className="elementor-post__badge">{p.categoryName}</div>
|
||||
) : null}
|
||||
<div className="elementor-post__text" style={{ padding: `0 ${textPad}` }}>
|
||||
<TitleTag className="elementor-post__title" style={{ fontSize: titleSize, marginBottom: titleSpacing }}>
|
||||
<a href={href}>{p.title}</a>
|
||||
</TitleTag>
|
||||
{showMeta && p.publishedAt ? (
|
||||
<div className="elementor-post__meta-data">
|
||||
<span className="elementor-post-date">{fmtDate(p.publishedAt)}</span>
|
||||
{showExcerpt ? (
|
||||
<div className="elementor-post__excerpt" style={{ marginBottom: excSpacing }}>
|
||||
<p
|
||||
style={{ color: excerptColor, fontSize: excerptSize }}
|
||||
dangerouslySetInnerHTML={{ __html: postExcerpt(p) }}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{showExcerpt ? (
|
||||
<p
|
||||
className="elementor-post__excerpt"
|
||||
style={{ color: excerptColor, marginBottom: excSpacing }}
|
||||
dangerouslySetInnerHTML={{ __html: postExcerpt(p) }}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
{showMeta && p.publishedAt ? (
|
||||
<div
|
||||
className="elementor-post__meta-data"
|
||||
style={{ padding: `10px ${textPad}`, fontSize: metaSize, fontWeight: metaWeight }}
|
||||
>
|
||||
<span className="elementor-post-date">{fmtDate(p.publishedAt)}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
@@ -1147,13 +1188,16 @@ function ButtonWidget({ el, settings }: { el: ElElement; settings: Record<string
|
||||
text
|
||||
);
|
||||
return (
|
||||
<div style={{ textAlign: (s(settings.align) as CSSProperties["textAlign"]) || "left" }}>
|
||||
<div style={{ textAlign: (s(settings.align) as CSSProperties["textAlign"]) || "left", ...(icon ? { paddingBottom: 2 } : {}) }}>
|
||||
{hoverBg || hoverColor ? (
|
||||
<style>{`[data-el="${el.id}"] .el-btn:hover{${hoverBg ? `background:${hoverBg};` : ""}${hoverColor ? `color:${hoverColor};` : ""}}`}</style>
|
||||
) : null}
|
||||
<a className="el-btn" href={link || "#"} style={style} target={(settings.link as { target?: string } | undefined)?.target === "_blank" ? "_blank" : undefined} rel="noreferrer">
|
||||
{content}
|
||||
</a>
|
||||
{/* 原站 wrapper 内含空白文本节点:inline-block 按钮所在行盒会叠加上正文 strut,
|
||||
父级高度 28(按钮 26 + 2px descender)。JSX 需显式补一个空格复现。 */}
|
||||
{" "}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1287,6 +1331,8 @@ function EaelInfoBoxWidget({ settings }: { settings: Record<string, unknown> })
|
||||
const title = s(settings.eael_infobox_title) || s(settings.title_text) || s(settings.title);
|
||||
const desc = s(settings.eael_infobox_text) || s(settings.description_text) || s(settings.description);
|
||||
const img = settings.eael_infobox_image as { url?: string; alt?: string } | undefined;
|
||||
const icon = settings.eael_infobox_icon_new as { value?: string; library?: string } | undefined;
|
||||
const iconSize = sizeCss(settings.eael_infobox_icon_size) || "26px";
|
||||
if (!title && !desc && !img?.url) return null;
|
||||
const tag = s(settings.eael_infobox_title_tag);
|
||||
const Tag = (["h1", "h2", "h3", "h4", "h5", "h6", "div"].includes(tag) ? tag : "div") as "div";
|
||||
@@ -1305,12 +1351,38 @@ function EaelInfoBoxWidget({ settings }: { settings: Record<string, unknown> })
|
||||
const tm = paddingCss(tmRaw);
|
||||
if (tm) titleStyle.margin = tm;
|
||||
}
|
||||
// 内容段落外距/颜色(EAEL 生成规则:eael_infobox_content_margin → p margin,
|
||||
// eael_infobox_content_color → p color;570df98 实测 margin 10px 0 0、#969696)
|
||||
const cmRaw = settings.eael_infobox_content_margin as Record<string, unknown> | undefined;
|
||||
const cmSet = !!cmRaw && ["top", "right", "bottom", "left"].some((k) => cmRaw[k] !== "" && cmRaw[k] != null);
|
||||
const contentVars: CSSProperties = {};
|
||||
if (cmSet) {
|
||||
const cm = paddingCss(cmRaw);
|
||||
if (cm) {
|
||||
const [t, r2, b, l] = cm.split(" ");
|
||||
Object.assign(contentVars, {
|
||||
"--ib-p-top": t ?? "0",
|
||||
"--ib-p-right": r2 ?? "0",
|
||||
"--ib-p-bottom": b ?? "0",
|
||||
"--ib-p-left": l ?? "0",
|
||||
});
|
||||
}
|
||||
}
|
||||
const contentColor = s(settings.eael_infobox_content_color);
|
||||
if (contentColor) Object.assign(contentVars, { "--ib-p-color": contentColor });
|
||||
return (
|
||||
<div className={cls}>
|
||||
{img?.url ? (
|
||||
<div className="infobox-icon">
|
||||
<img src={img.url} alt={s(img.alt)} loading="lazy" />
|
||||
</div>
|
||||
) : icon?.value ? (
|
||||
// 图标型 info-box(df5b95b:fas fa-check-circle 26px,左侧图标 + 标题)
|
||||
<div className="infobox-icon">
|
||||
<div className="infobox-icon-wrap">
|
||||
<i className={icon.value} style={{ fontSize: iconSize, lineHeight: 1 }} aria-hidden="true" />
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="infobox-content">
|
||||
{title ? (
|
||||
@@ -1321,7 +1393,7 @@ function EaelInfoBoxWidget({ settings }: { settings: Record<string, unknown> })
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{desc ? <div dangerouslySetInnerHTML={{ __html: desc }} /> : null}
|
||||
{desc ? <div className="infobox-text" style={Object.keys(contentVars).length ? contentVars : undefined} dangerouslySetInnerHTML={{ __html: desc }} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -1390,7 +1462,7 @@ function AdvTabsWidget({ el, settings }: { el: ElElement; settings: Record<strin
|
||||
// 内容面板 id:标题 slug 化(原站 building-automation / building-automation-tab 等)
|
||||
const slug = (t: string) => t.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-");
|
||||
return (
|
||||
<div id={`eael-advance-tabs-${el.id}`} className="eael-advance-tabs eael-tabs-horizontal">
|
||||
<div id={`eael-advance-tabs-${el.id}`} className="eael-advance-tabs eael-tabs-horizontal eael-tab-auto-active active-caret-on">
|
||||
<div className="eael-tabs-nav">
|
||||
<ul role="tablist">
|
||||
{items.map((t, i) => (
|
||||
@@ -1644,7 +1716,120 @@ function IconWidget({ settings }: { settings: Record<string, unknown> }) {
|
||||
);
|
||||
}
|
||||
|
||||
/** # 组件 toggle / accordion widget:手风琴 */
|
||||
/** # 组件 Elementor 原生 toggle widget(FAQ 折叠问答)
|
||||
* DOM 严格对齐原站(smart-gateways 846aeb5 实测 2026-11-02):
|
||||
* .elementor-toggle > .elementor-toggle-item > h3.elementor-tab-title
|
||||
* (> .elementor-toggle-icon > closed/opened 两个 span SVG + a.elementor-toggle-title)
|
||||
* + div.elementor-tab-content。
|
||||
* 交互:Elementor 默认全部收起、各条目独立开合;active 用 elementor-active 类,
|
||||
* 收起/展开的图标与内容显隐由 widget-toggle.min.css 对应规则完成(app.css「Elementor toggle」)。
|
||||
* 开启 faq_schema 时在原位置输出 FAQPage JSON-LD(与原站一致)。
|
||||
*/
|
||||
function ToggleWidget({ el, settings }: { el: ElElement; settings: Record<string, unknown> }) {
|
||||
const tabs = Array.isArray(settings.tabs) ? (settings.tabs as { tab_title?: string; tab_content?: string }[]) : [];
|
||||
/** # 成员变量 已展开条目的索引集合(默认空:全部收起) **/
|
||||
const [openSet, setOpenSet] = useState<Set<number>>(() => new Set());
|
||||
if (!tabs.length) return null;
|
||||
// 锚点 id 基数:原站为 Elementor 渲染计数器(1381…);本地用 widget id 的数值化
|
||||
// 保证 SSR/水合稳定且同页多个 toggle 不冲突,仅影响锚点,无视觉作用
|
||||
const base = parseInt(el.id ?? "0", 16) || 0;
|
||||
const titleTag = s(settings.title_html_tag) || "h3";
|
||||
const TitleTag = titleTag as "h3";
|
||||
const iconValue = s((settings.selected_icon as { value?: string } | undefined)?.value) || "fas fa-caret-down";
|
||||
const isCaret = iconValue.includes("caret");
|
||||
/** # 函数 切换单个条目(Elementor toggle 允许同时展开多项) */
|
||||
const toggle = (i: number) => {
|
||||
setOpenSet((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(i)) next.delete(i);
|
||||
else next.add(i);
|
||||
return next;
|
||||
});
|
||||
};
|
||||
const faq = s(settings.faq_schema) === "yes";
|
||||
return (
|
||||
<>
|
||||
<div className="elementor-toggle" style={{ paddingBottom: 4 }}>
|
||||
{/* 原站 widget 元素比其容器高 4px(容器 310 + 4),桌面 314/移动 424;
|
||||
该 4px 为 Elementor 布局尾巴,补在 toggle 底部以保持节高与后续元素位置一致 */}
|
||||
{tabs.map((t, i) => {
|
||||
const active = openSet.has(i);
|
||||
const titleId = `elementor-tab-title-${base}${i + 1}`;
|
||||
const contentId = `elementor-tab-content-${base}${i + 1}`;
|
||||
return (
|
||||
<div className="elementor-toggle-item" key={i}>
|
||||
<TitleTag
|
||||
id={titleId}
|
||||
className={`elementor-tab-title${active ? " elementor-active" : ""}`}
|
||||
data-tab={i + 1}
|
||||
role="button"
|
||||
aria-controls={contentId}
|
||||
aria-expanded={active ? "true" : "false"}
|
||||
onClick={() => toggle(i)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault();
|
||||
toggle(i);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="elementor-toggle-icon elementor-toggle-icon-left" aria-hidden="true">
|
||||
<span className="elementor-toggle-icon-closed">
|
||||
{isCaret ? (
|
||||
<svg className="e-font-icon-svg e-fas-caret-down" viewBox="0 0 320 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z" />
|
||||
</svg>
|
||||
) : (
|
||||
<i className={iconValue} />
|
||||
)}
|
||||
</span>
|
||||
<span className="elementor-toggle-icon-opened">
|
||||
{isCaret ? (
|
||||
<svg className="elementor-toggle-icon-opened e-font-icon-svg e-fas-caret-up" viewBox="0 0 320 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M288.662 352H31.338c-17.818 0-26.741-21.543-14.142-34.142l128.662-128.662c7.81-7.81 20.474-7.81 28.284 0l128.662 128.662c12.6 12.599 3.676 34.142-14.142 34.142z" />
|
||||
</svg>
|
||||
) : (
|
||||
<i className={iconValue} />
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
<a className="elementor-toggle-title" tabIndex={0}>
|
||||
{s(t.tab_title)}
|
||||
</a>
|
||||
</TitleTag>
|
||||
<div
|
||||
id={contentId}
|
||||
className={`elementor-tab-content elementor-clearfix${active ? " elementor-active" : ""}`}
|
||||
data-tab={i + 1}
|
||||
role="region"
|
||||
aria-labelledby={titleId}
|
||||
dangerouslySetInnerHTML={{ __html: s(t.tab_content) }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{faq ? (
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "FAQPage",
|
||||
mainEntity: tabs.map((t) => ({
|
||||
"@type": "Question",
|
||||
name: s(t.tab_title),
|
||||
acceptedAnswer: { "@type": "Answer", text: s(t.tab_content) },
|
||||
})),
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/** # 组件 toggle(Pro accordion)旧实现:Radix 手风琴(待独立页面审计后替换) */
|
||||
function AccordionWidget({ settings }: { settings: Record<string, unknown> }) {
|
||||
const tabs = Array.isArray(settings.tabs) ? (settings.tabs as { tab_title?: string; tab_content?: string }[]) : [];
|
||||
if (!tabs.length) return null;
|
||||
|
||||
@@ -132,12 +132,15 @@ const SETTING_KEYS = new Set([
|
||||
"youtube_url",
|
||||
"map",
|
||||
"posts_per_page",
|
||||
// shortcode widget 源码([elementor-template id="N"] 内嵌模板、页脚菜单/gtranslate)
|
||||
"shortcode",
|
||||
"_skin",
|
||||
"posts_include",
|
||||
"posts_include_term_ids",
|
||||
"cards_posts_per_page",
|
||||
"cards_columns",
|
||||
"cards_columns_tablet",
|
||||
"cards_columns_mobile",
|
||||
"cards_row_gap",
|
||||
"cards_column_gap",
|
||||
"cards_meta_data",
|
||||
@@ -158,6 +161,16 @@ const SETTING_KEYS = new Set([
|
||||
"cards_read_more_alignment",
|
||||
"cards_title_color",
|
||||
"cards_card_bg_color",
|
||||
"cards_card_border_color",
|
||||
"cards_meta_separator",
|
||||
// cards 皮肤排版与缩略图比率(H18 meta 1rem/300、标题 1.125rem、移动比率 0.67;
|
||||
// 模板 27143 默认 meta 12px、比率 0.66/0.6)
|
||||
"cards_meta_typography_font_size",
|
||||
"cards_meta_typography_font_weight",
|
||||
"cards_title_typography_font_size",
|
||||
"cards_title_typography_font_weight",
|
||||
"cards_excerpt_typography_font_size",
|
||||
"cards_item_ratio",
|
||||
"cards_item_ratio_mobile",
|
||||
"post_type",
|
||||
"eael_ticker_tag_text",
|
||||
@@ -168,16 +181,22 @@ const SETTING_KEYS = new Set([
|
||||
"eael_infobox_text",
|
||||
"eael_infobox_title_tag",
|
||||
"eael_infobox_image",
|
||||
"eael_infobox_img_or_icon",
|
||||
"eael_infobox_icon_new",
|
||||
"eael_infobox_icon_size",
|
||||
"eael_infobox_img_type",
|
||||
"eael_infobox_img_margin",
|
||||
"eael_infobox_title_margin",
|
||||
"eael_infobox_title_margin_mobile",
|
||||
"eael_infobox_title_typography_typography",
|
||||
"eael_infobox_title_typography_font_size",
|
||||
"eael_infobox_title_typography_line_height",
|
||||
"eael_infobox_title_typography_font_size_mobile",
|
||||
"eael_infobox_title_typography_font_size_tablet",
|
||||
"eael_infobox_title_typography_font_weight",
|
||||
"eael_infobox_title_color",
|
||||
"eael_infobox_content_margin",
|
||||
"eael_infobox_content_color",
|
||||
"hover_animation",
|
||||
// widget 高级盒模型(如 eael-info-box 边框卡:_border_* + _padding/_margin)
|
||||
"_margin",
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* # 模块 catch-all 页面共享逻辑($.tsx 只导出 Route)
|
||||
*/
|
||||
import { getPublishedContent, listPosts, type PostListItem, type ProductListResult } from "~/server/data";
|
||||
import { getPublishedContent, listPosts, type PostCardItem, type PostListItem, type ProductListResult } from "~/server/data";
|
||||
import type { LayoutData } from "~/components/website/Layout";
|
||||
import type { ElElement } from "~/components/elementor/Renderer";
|
||||
|
||||
/** # 类型 兜底 loader 返回结构 */
|
||||
export type CatchAllData = {
|
||||
@@ -28,6 +29,10 @@ export type CatchAllData = {
|
||||
hotProducts: ProductListResult;
|
||||
/** # 成员变量 WP /%category%/ 归档命中:分类 + 分页文章(content 为 null 时可能命中) **/
|
||||
archive: null | { name: string; slug: string; posts: { items: PostListItem[]; total: number; page: number; pageSize: number } };
|
||||
/** # 成员变量 内嵌 Elementor 模板:shortcode widget id → 模板元素树 **/
|
||||
templates: Record<string, ElElement[]>;
|
||||
/** # 成员变量 posts widget 预取文章:widget id → 文章卡 **/
|
||||
postsData: Record<string, PostCardItem[]>;
|
||||
};
|
||||
|
||||
/** # 类型 归档分页结果(listPosts 返回形状的窄化别名) */
|
||||
|
||||
Reference in New Issue
Block a user