v0.10.19: G2 页脚 Footer 一比一复刻(原站 elementor_library post 58)

- 导入脚本 scripts/import-footer-template.ts:从 legacy dump 提取 post 58 的 _elementor_data
  写入 contents(type=template, slug=footer),并按线上 post-58.css/DOM 补齐快照后改动
  (f95e22e Google Preferred Source、405d251 width:auto、2481635 桌面外距)
- Layout/getLayoutData 下发 footerDoc,Footer 改用 ElementorRenderer 直出原模板,
  短代码按 widget id 注入:menu-2 移动端折叠菜单、gtranslate 语言下拉(原生 select.gt_selector)
- Renderer 增强:section html_tag/边框/content_position、column 响应式宽度与
  populated padding/margin(tablet/mobile)、widget 响应式盒模型、icon widget(fixed 返回顶部)、
  icon-list 图标/间距/文字排版与 CSS 变量悬停、image 宽度、divider Elementor DOM、
  text-editor 对齐与 current-date-time 动态年份、shortcode 预渲染上下文
- slim 白名单补齐响应式/图标/动态标签字段;app.css 新增 FA Solid/Regular 字体、
  页脚 scoped 规则、elementor-hidden-* 响应式可见性
- 实测 1436:页脚总高 590.4 与原站全等(列宽/内层列/logo/社交/下拉逐项一致);
  390:56/15/347/337/97/46.4/61 逐项全等;语言切换写 ui_lang cookie;返回顶部回滚 0
- bun test 11 pass;版本 0.10.19
This commit is contained in:
bigcode
2026-11-02 11:20:29 +08:00
parent 686dbe18de
commit a7614e5485
12 changed files with 765 additions and 93 deletions
+20 -7
View File
@@ -1,13 +1,14 @@
/**
* # 组件 网站公共布局 Layout
* 组合 Header + 页面内容 + Footer;样式入口(Tailwind v4 + shadcn/ui)在此引入一次。
* 布局数据(菜单/分类)由各路由 loader 通过 getLayoutData() 取得后传入。
* 布局数据(菜单/分类/页脚 Elementor 模板)由各路由 loader 通过 getLayoutData() 取得后传入。
*/
import "../../styles/app.css";
import { Header } from "./Header";
import { Footer, type FooterCategory } from "./Footer";
import { createServerFn } from "@tanstack/react-start";
import { getMenuTree, getProductCategoryTree, type MenuNode } from "~/server/data";
import { getFooterTemplate, getMenuTree, getProductCategoryTree, type MenuNode } from "~/server/data";
import type { ElElement } from "~/components/elementor/Renderer";
/** # 类型 布局数据(Header/Footer 所需) */
export type LayoutData = {
@@ -17,12 +18,24 @@ export type LayoutData = {
footerMenu: MenuNode[];
/** # 成员变量 产品分类树(含计数) **/
categories: FooterCategory[];
/** # 成员变量 页脚 Elementor 模板文档(原站 elementor_library post 58) **/
footerDoc: ElElement[] | null;
};
/** # 函数 Server Function:获取布局数据(菜单 + 分类) */
export const getLayoutData = createServerFn({ method: "GET" }).handler(async (): Promise<LayoutData> => {
const [menu, footerMenu, cats] = await Promise.all([getMenuTree("max_mega_menu_1"), getMenuTree("menu-2"), getProductCategoryTree()]);
return { menu, footerMenu, categories: cats };
/** # 函数 Server Function:获取布局数据(菜单 + 分类 + 页脚模板)
* 返回类型标 any:Elementor 文档的 settings 是递归 unknown(JSON),TanStack 的
* 序列化静态校验无法展开递归 Record,会误报不可序列化;运行时就是纯 JSON。
*/
export const getLayoutData = createServerFn({ method: "GET" }).handler(async (): Promise<any> => {
const [menu, footerMenu, cats, footer] = await Promise.all([
getMenuTree("max_mega_menu_1"),
getMenuTree("menu-2"),
getProductCategoryTree(),
getFooterTemplate().catch(() => null),
]);
const el = (footer?.meta as { elementor?: unknown } | null)?.elementor;
const footerDoc = Array.isArray(el) ? (el as ElElement[]) : null;
return { menu, footerMenu, categories: cats, footerDoc } satisfies LayoutData;
});
/** # 类型 Layout 属性 */
@@ -31,7 +44,7 @@ export function Layout(props: { layout: LayoutData; children: React.ReactNode })
<>
<Header menu={props.layout.menu} />
<main>{props.children}</main>
<Footer categories={props.layout.categories} menu={props.layout.footerMenu} />
<Footer categories={props.layout.categories} menu={props.layout.footerMenu} doc={props.layout.footerDoc} />
</>
);
}