Files
dusuniot/scripts/import-footer-template.ts
T
bigcode a7614e5485 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
2026-11-02 11:20:29 +08:00

125 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* # 脚本 导入页脚 Elementor 模板(legacy elementor_library post 58)
* 背景:WordPress 导入管线未迁移 elementor_library 类型(仅 page/post/landing),
* 页脚模板(post 58,含 46d7afa / d834160 两节及其内嵌 28c7f0b8 / a428bb4)因此缺失。
* 数据源:legacy/dusuniot-db-20261029.sql 中 `(meta_id,58,'_elementor_data','…')` 行。
* 补丁:线上页脚另有 f95e22e(Google Preferred Source 图,dump 快照之后新增),
* 依线上 DOM(/tmp/orig-footer.html 取证:class elementor-widget__width-inherit、
* img 528×64、链接 google.com/preferences/source?q=dusuniot.com)补入 2ccb47d 列末位。
* 结果写入 contents(type=template, slug=footer, meta.elementor),供 Layout 渲染。
* 运行:bun scripts/import-footer-template.ts
*/
import { SQL } from "bun";
const DUMP = "/home/bigcode/data/dev/sites/dusuniot/legacy/dusuniot-db-20261029.sql";
const db = new SQL("postgres://dusuniot:dusuniot@127.0.0.1:25432/dusuniot");
/** # 函数 从 dump 提取 post 58 的 _elementor_data 并反转义为 JSON
* 返回值 Elementor 元素数组
*/
async function extractTemplate(): Promise<Record<string, unknown>[]> {
const text = await Bun.file(DUMP).text();
const m = text.match(/\(\d+,58,'_elementor_data','((?:[^'\\]|\\.)*)'\)/);
if (!m) throw new Error("dump 中未找到 post 58 的 _elementor_data");
const raw = m[1]!;
// SQL dump 反斜杠转义:\" → "、\' → '、\\ → \
const json = raw.replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
const data = JSON.parse(json);
if (!Array.isArray(data)) throw new Error("_elementor_data 不是数组");
return data as Record<string, unknown>[];
}
/** # 函数 深度查找指定 id 的元素节点 */
function findElement(els: Record<string, unknown>[], id: string): Record<string, unknown> | null {
for (const el of els) {
if (el.id === id) return el;
const children = el.elements as Record<string, unknown>[] | undefined;
if (children?.length) {
const hit = findElement(children, id);
if (hit) return hit;
}
}
return null;
}
/** # 函数 线上快照新增的 f95e22e(Google Preferred Source 图)
* 线上 post-58.css 取证:.elementor-element-f95e22e img{width:300px}、
* > .elementor-widget-container{margin:10px 0 0};dump 快照之后新增,故在此补全。
*/
function preferredSourceWidget(): Record<string, unknown> {
return {
id: "f95e22e",
elType: "widget",
settings: {
image: {
url: "http://www.dusuniot.com/wp-content/uploads/2022/06/google-preffered-source.png",
id: 161805,
alt: "google prefferd source",
source: "library",
size: "",
},
image_size: "full",
width: { unit: "px", size: 300, sizes: [] },
align: "left",
_element_width: "inherit",
_margin: { unit: "px", top: "10", right: "0", bottom: "0", left: "0", isLinked: false },
link: {
url: "https://www.google.com/preferences/source?q=dusuniot.com",
is_external: "on",
nofollow: "on",
custom_attributes: "",
},
},
elements: [],
widgetType: "image",
};
}
const elements = await extractTemplate();
// 补丁:2ccb47d 列末尾追加 f95e22e(幂等:已存在则跳过)
const col = findElement(elements, "2ccb47d");
if (!col) throw new Error("模板缺少 2ccb47d 列");
const colChildren = (col.elements as Record<string, unknown>[]) || [];
const psIdx = colChildren.findIndex((el) => el.id === "f95e22e");
if (psIdx >= 0) colChildren[psIdx] = preferredSourceWidget();
else colChildren.push(preferredSourceWidget());
col.elements = colChildren;
// 补丁:社交图标行 405d251 宽度模式(线上 class=elementor-widget__width-auto、
// 生成 CSS width:auto;dump 快照为 inherit,实测线上为 auto → 与 gtranslate 同排)
const social = findElement(elements, "405d251");
if (social) {
const st = (social.settings as Record<string, unknown>) || {};
st._element_width = "auto";
social.settings = st;
}
// 补丁:gtranslate 短代码控件 2481635 桌面外距(线上 post-58.css:margin -10px 0 0 20px;
// dump 只有 _margin_mobile,桌面值来自快照之后的编辑)
const gt = findElement(elements, "2481635");
if (gt) {
const st = (gt.settings as Record<string, unknown>) || {};
st._margin = { unit: "px", top: "-10", right: "0", bottom: "0", left: "20", isLinked: false };
gt.settings = st;
}
const meta = JSON.stringify({ elementor: elements });
// 写入 contents:type=template / slug=footer;已存在则更新
const existing = await db`SELECT id FROM contents WHERE slug='footer' AND type='template'`;
if (existing.length) {
await db`UPDATE contents SET meta=${meta}::jsonb, status='published', updated_at=now() WHERE id=${existing[0]!.id}`;
console.log("已更新 footer 模板,contents.id =", existing[0]!.id);
} else {
const inserted = await db`
INSERT INTO contents (type, slug, title, status, meta, legacy_id, published_at)
VALUES ('template', 'footer', 'Footer', 'published', ${meta}::jsonb, 58, now())
RETURNING id`;
console.log("已插入 footer 模板,contents.id =", inserted[0]!.id);
}
// 验证:节数量与顶层 id
const top = elements.map((el) => el.id).join(", ");
console.log("顶层节:", top);
await db.end();