v0.10.25: /solution/ 归档+落地页复刻(archive 组件/分页/摘录24词)、Header 全断点对齐、page_css 导入管线、reviews/populated 宽度/_css_classes/tabs HTML/图片变体修复
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* # 模块 Elementor 图片 WP 尺寸变体解析(仅服务端)
|
||||
* 原站 image widget 的 image_size=medium/thumbnail/... 时 WP 渲染 -300x272 这类裁剪/缩放
|
||||
* 变体文件;dump 的 image.url 只存原图。这里在服务端按 uploads 目录里实际存在的
|
||||
* `<base>-<w>x<h>.<ext>` 变体择优改写 image.url(选宽度 ≤ 目标里最大的;无变体保持原图)。
|
||||
*/
|
||||
import { readdirSync } from "node:fs";
|
||||
import { join, dirname } from "node:path";
|
||||
|
||||
/** # 常量 WP 尺寸名 → 目标宽度(Elementor 常用档位) **/
|
||||
const SIZE_TARGET: Record<string, number> = {
|
||||
thumbnail: 150,
|
||||
medium: 300,
|
||||
medium_large: 768,
|
||||
large: 1024,
|
||||
};
|
||||
|
||||
/** # 成员变量 目录列表缓存(同一目录只扫一次盘) **/
|
||||
const dirCache = new Map<string, string[]>();
|
||||
|
||||
/** # 成员变量 变体解析结果缓存(url+size → 最终 url) **/
|
||||
const resultCache = new Map<string, string>();
|
||||
|
||||
/** # 函数 查找某原图在给定目标宽度下的最佳变体 URL(无则返回 null)
|
||||
* 参数 url 原图 URL(本地 /uploads/... 或原站绝对地址);targetWidth WP 档位目标宽
|
||||
*/
|
||||
function findVariant(url: string, targetWidth: number): string | null {
|
||||
// 归一化到本地 /uploads/ 路径(dump 里可能存原站绝对地址)
|
||||
const up = url.indexOf("/uploads/");
|
||||
if (up === -1) return null;
|
||||
const localPath = url.slice(up); // /uploads/2022/06/Group-201.png
|
||||
const m = localPath.match(/^(.*\/)([^/]+)\.(\w+)$/);
|
||||
if (!m) return null;
|
||||
const [, dir, base, ext] = m;
|
||||
const fsDir = join("public", dir);
|
||||
let files = dirCache.get(fsDir);
|
||||
if (!files) {
|
||||
try {
|
||||
files = readdirSync(fsDir);
|
||||
} catch {
|
||||
files = [];
|
||||
}
|
||||
dirCache.set(fsDir, files);
|
||||
}
|
||||
// 候选:<base>-<w>x<h>.<ext>;取宽度 ≤ 目标里最大的(同宽取更矮的,WP medium 缩放优先于裁剪)
|
||||
const prefix = `${base}-`;
|
||||
let best: { w: number; h: number; file: string } | null = null;
|
||||
for (const f of files) {
|
||||
const fm = f.match(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(\\d+)x(\\d+)\\.${ext}$`));
|
||||
if (!fm) continue;
|
||||
const w = Number(fm[1]);
|
||||
const h = Number(fm[2]);
|
||||
if (w > targetWidth) continue;
|
||||
if (!best || w > best.w || (w === best.w && h < best.h)) best = { w, h, file: f };
|
||||
}
|
||||
return best ? `${dir}${best.file}` : null;
|
||||
}
|
||||
|
||||
/** # 函数 就地解析文档内 image widget 的尺寸变体(服务端 loader 调用;浏览器端空操作)
|
||||
* 参数 doc elementor 文档(数组或 {elements} 包装),image.url 被改写为变体地址
|
||||
*/
|
||||
export function resolveImageVariants(doc: unknown): void {
|
||||
if (typeof window !== "undefined") return;
|
||||
const walk = (el: unknown) => {
|
||||
if (Array.isArray(el)) {
|
||||
for (const x of el) walk(x);
|
||||
return;
|
||||
}
|
||||
if (!el || typeof el !== "object") return;
|
||||
const e = el as { id?: unknown; widgetType?: unknown; settings?: Record<string, unknown>; elements?: unknown[] };
|
||||
if (e.widgetType === "image") {
|
||||
const st = e.settings ?? {};
|
||||
const target = SIZE_TARGET[String(st.image_size ?? "")];
|
||||
const img = st.image as { url?: string } | undefined;
|
||||
if (target && img?.url && img.url.includes("/uploads/")) {
|
||||
const key = `${img.url}|${target}`;
|
||||
let resolved = resultCache.get(key);
|
||||
if (resolved === undefined) {
|
||||
resolved = findVariant(img.url, target) ?? "";
|
||||
resultCache.set(key, resolved);
|
||||
}
|
||||
if (resolved) img.url = resolved;
|
||||
}
|
||||
}
|
||||
for (const c of Array.isArray(e.elements) ? e.elements : []) walk(c);
|
||||
};
|
||||
walk(doc);
|
||||
}
|
||||
Reference in New Issue
Block a user