/** * @fileoverview 占位照片生成脚本。 * * 文件用途: * - 依据 src/data/spots.js 的照片清单,批量生成 4:3 手账风占位 SVG, * 供照片未就绪期的开发与演示使用。 * * 核心职责: * 1. 读取景点配置,为每张照片生成 `public/assets/photos/_<序号>.svg`; * 2. 占位视觉:暖色渐变 + 景点图标 + 名称与 caption 水印 + 虚线相框; * 3. 文件已存在时跳过(--force 强制重生成)。 * * 主要流程: * node scripts/gen-placeholder-photos.mjs [--force] → 遍历 spots → * 写出 SVG → 控制台汇总生成结果。 * * 用法: * node scripts/gen-placeholder-photos.mjs # 仅生成缺失文件 * node scripts/gen-placeholder-photos.mjs --force # 强制重新生成全部 */ import { mkdirSync, writeFileSync, existsSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, resolve } from 'node:path'; import { SPOTS } from '../src/data/spots.js'; /** 脚本所在目录。 */ const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url)); /** 项目根目录。 */ const PROJECT_ROOT = resolve(SCRIPT_DIR, '..'); /** 照片输出目录。 */ const OUT_DIR = resolve(PROJECT_ROOT, 'public/assets/photos'); /** 占位卡宽度(像素)。 */ const W = 1200; /** 占位卡高度(像素,4:3)。 */ const H = 900; /** * 按景点 id 生成稳定的色相(0-360)。 * * @param {string} id 景点 id。 * @returns {number} 色相角度。 */ function hueOf(id) { let hash = 0; for (const ch of id) { hash = (hash * 31 + ch.charCodeAt(0)) % 360; } return hash; } /** * 生成单张占位照片 SVG 内容。 * * @param {Object} spot 景点配置。 * @param {Object} photo 照片配置(含 caption)。 * @returns {string} SVG 文本。 */ function buildSvg(spot, photo) { const hue = hueOf(spot.id); const icon = spot.icon; const escapedCaption = photo.caption .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>'); return ` ${icon} ${escapedCaption} · 占位照片 · `; } /** * 脚本入口:遍历景点生成占位照片。 * * @returns {Promise} 完成后输出汇总。 */ async function main() { const force = process.argv.includes('--force'); mkdirSync(OUT_DIR, { recursive: true }); let created = 0; let skipped = 0; for (const spot of SPOTS) { spot.photos.forEach((photo, index) => { const fileName = photo.src.split('/').pop(); const outPath = resolve(OUT_DIR, fileName); if (!force && existsSync(outPath)) { skipped += 1; return; } writeFileSync(outPath, buildSvg(spot, photo), 'utf8'); created += 1; console.log(`[photos] 生成 ${fileName}`); }); } console.log(`[photos] 完成:新建 ${created} 张,跳过已存在 ${skipped} 张`); } main().catch((err) => { console.error('[photos] 生成失败:', err); process.exit(1); });