/** * 生成后端种子静态资源:D:/bsch/cms/upload/seed/ * - SVG 场景插画(校园主题分层构图:天空渐变/山峦/建筑剪影/树木/水面;无占位文字) * - 最小合法 PDF(附件演示可真实打开) * 轮播 1920×560 对齐主题包设计尺寸约定(themes 下的 theme.json bannerSize)。 * 用法:node seed-assets.cjs */ const fs = require("fs"); const path = require("path"); const OUT = "D:/bsch/cms/upload/seed"; fs.mkdirSync(OUT, { recursive: true }); function hashCode(s) { let h = 0; for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0; return Math.abs(h); } /** 确定性伪随机(同名资源每次生成完全一致,保证截图可复现) */ function rng(seed) { let s = seed || 1; return () => { s = (s * 1664525 + 1013904223) | 0; return ((s >>> 8) & 0xffff) / 0xffff; }; } /** 一栋建筑剪影(含窗户点阵) */ function building(x, baseY, w, h, fill, winFill, r) { const cols = Math.max(2, Math.floor(w / 26)); const rows = Math.max(2, Math.floor(h / 30)); let wins = ""; for (let i = 0; i < cols; i++) { for (let j = 0; j < rows; j++) { if (r() < 0.25) continue; // 部分窗户熄灭,更像真实夜景/黄昏 wins += ``; } } return `${wins}`; } /** 一棵树(圆形树冠 + 矩形树干) */ function tree(x, baseY, scale, leaf, trunk) { const r = 16 * scale; return ` `; } /** 山峦剪影(两座) */ function mountains(w, baseY, c1, c2, r) { const x1 = w * (0.05 + r() * 0.2); const x2 = w * (0.55 + r() * 0.25); const m1 = `M ${x1} ${baseY} L ${x1 + w * 0.22} ${baseY - 90 - r() * 70} L ${x1 + w * 0.46} ${baseY} Z`; const m2 = `M ${x2} ${baseY} L ${x2 + w * 0.2} ${baseY - 70 - r() * 60} L ${x2 + w * 0.42} ${baseY} Z`; return ``; } /** 云(椭圆组) */ function cloud(cx, cy, scale, fill) { return ` `; } /** 完整校园场景(黄昏/清晨/白天三种氛围可选) */ function campusScene(name, w, h, palette, opts = {}) { const seed = hashCode(name); const r = rng(seed); const p = { sky1: palette[0], sky2: palette[1], sun: palette[2] || "#fff", far: palette[3] || "rgba(255,255,255,0.28)", near: palette[4] || "rgba(20,40,70,0.35)", ground: palette[5] || "#2c4a6e", water: palette[6] || null, building: palette[7] || "rgba(16,34,60,0.85)", win: palette[8] || "rgba(255,224,150,0.9)", leaf: palette[9] || "rgba(46,94,60,0.9)", trunk: palette[10] || "rgba(66,48,30,0.9)", cloud: palette[11] || "rgba(255,255,255,0.5)", }; const horizon = h * (opts.horizon || 0.72); let out = ` `; // 太阳/月亮 out += ``; // 云 out += cloud(w * (0.12 + r() * 0.25), h * (0.12 + r() * 0.15), 0.9 + r() * 0.5, p.cloud); out += cloud(w * (0.6 + r() * 0.3), h * (0.08 + r() * 0.12), 0.7 + r() * 0.4, p.cloud); // 远山 out += mountains(w, horizon, p.far, p.far, r); // 教学楼主群(画面中偏右) const groundY = opts.water ? horizon + (h - horizon) * 0.42 : horizon; const bx = w * (opts.buildingX || 0.42); out += building(bx, groundY, w * 0.11, (h - groundY) * (1.15 + r() * 0.5), p.building, p.win, r); out += building(bx + w * 0.115, groundY, w * 0.075, (h - groundY) * (0.85 + r() * 0.3), p.building, p.win, r); out += building(bx - w * 0.062, groundY, w * 0.058, (h - groundY) * (0.7 + r() * 0.25), p.building, p.win, r); // 钟楼(细高塔 + 尖顶) const tx = bx + w * 0.2; const th = (h - groundY) * (1.5 + r() * 0.3); out += ` `; // 地面 if (opts.water) { out += ``; out += ``; // 水面倒影横线 for (let i = 0; i < 5; i++) { out += ``; } } else { out += ``; } // 行道树 const treeY = opts.water ? groundY + (h - groundY) * 0.22 : horizon + (h - horizon) * 0.18; for (let i = 0; i < 6; i++) { const tx2 = w * (0.03 + i * 0.165 + r() * 0.04); if (tx2 > bx - w * 0.08 && tx2 < tx + w * 0.05 && !opts.water) continue; // 避开建筑 out += tree(tx2, treeY, 1 + r() * 0.5, p.leaf, p.trunk); } // 前景草坪弧线 out += ``; out += ``; return out; } /** 校徽(圆形徽章:书本 + 星 + 环形留白,透明背景适配页头) */ function emblem() { return ` `; } /* ---- 轮播(1920×560,对齐 theme.json bannerSize 约定)---- */ fs.writeFileSync(path.join(OUT, "banner-1.svg"), campusScene("banner-1", 1920, 560, ["#1c3f68", "#7fb2e5", "#fff2c9", "rgba(255,255,255,0.22)", "rgba(20,45,80,0.30)", "#27517f"], { water: true, buildingX: 0.40 })); fs.writeFileSync(path.join(OUT, "banner-2.svg"), campusScene("banner-2", 1920, 560, ["#0e4f4a", "#8fd0b0", "#fdeec1", "rgba(255,255,255,0.20)", "rgba(10,60,50,0.32)", "#1f6b52", "#2f8a68", "rgba(8,52,44,0.88)", "rgba(255,240,190,0.92)", "rgba(24,92,64,0.95)", "rgba(52,40,24,0.95)", "rgba(255,255,255,0.42)"], { water: true, buildingX: 0.46 })); fs.writeFileSync(path.join(OUT, "banner-3.svg"), campusScene("banner-3", 1920, 560, ["#3a1f5d", "#e08a6d", "#ffd9a0", "rgba(255,214,170,0.30)", "rgba(52,24,70,0.38)", "#4a2a63", "#6b3d84", "rgba(30,14,48,0.9)", "rgba(255,226,170,0.95)", "rgba(96,60,110,0.9)", "rgba(50,32,24,0.9)", "rgba(255,235,210,0.4)"], { horizon: 0.78, buildingX: 0.36 })); fs.writeFileSync(path.join(OUT, "banner-4.svg"), campusScene("banner-4", 1920, 560, ["#141c3a", "#4f7fd9", "#cfe0ff", "rgba(190,210,255,0.20)", "rgba(12,20,48,0.42)", "#1b2a52"], { horizon: 0.75, buildingX: 0.44 })); /* ---- 文章封面(640×360)---- */ fs.writeFileSync(path.join(OUT, "cover-运动会.svg"), campusScene("cover-sports", 640, 360, ["#0d3b66", "#69a8d8", "#fff3c4", "rgba(255,255,255,0.24)", "rgba(10,40,70,0.34)", "#1c5080", "#2e6da4", "rgba(10,34,62,0.9)", "rgba(255,230,160,0.92)", "rgba(30,90,70,0.95)", "rgba(50,38,22,0.95)", "rgba(255,255,255,0.5)"], { water: true })); fs.writeFileSync(path.join(OUT, "cover-迎新晚会.svg"), campusScene("cover-gala", 640, 360, ["#2a0e4e", "#c25b8a", "#ffe3a0", "rgba(255,200,220,0.26)", "rgba(40,12,64,0.4)", "#3c1a5c", "#57297a", "rgba(26,8,44,0.92)", "rgba(255,224,170,0.95)", "rgba(120,60,130,0.9)", "rgba(48,30,20,0.9)", "rgba(255,230,240,0.4)"], { horizon: 0.8, buildingX: 0.42 })); fs.writeFileSync(path.join(OUT, "cover-创新创业.svg"), campusScene("cover-inno", 640, 360, ["#03363d", "#4fd1c5", "#eafff9", "rgba(220,255,250,0.22)", "rgba(4,50,56,0.36)", "#065a63", "#0b7d85", "rgba(4,42,48,0.9)", "rgba(210,255,245,0.92)", "rgba(10,92,80,0.9)", "rgba(40,40,22,0.9)", "rgba(235,255,252,0.4)"], { water: true })); /* ---- 正文配图(640×360)---- */ ["art-1", "art-2", "art-3", "art-4", "art-5", "art-6"].forEach((n, i) => { const palettes = [ ["#1c3f68", "#7fb2e5", "#fff2c9", "rgba(255,255,255,0.22)", "rgba(20,45,80,0.30)", "#27517f"], ["#5d2019", "#e8a06a", "#fff0d0", "rgba(255,220,190,0.26)", "rgba(80,28,20,0.34)", "#7a3020", null, "rgba(70,24,16,0.9)", "rgba(255,235,200,0.92)", "rgba(160,70,40,0.9)", "rgba(56,36,20,0.9)", "rgba(255,240,220,0.42)"], ["#23306b", "#9fb4f0", "#f4f6ff", "rgba(230,235,255,0.24)", "rgba(24,32,86,0.32)", "#303f8c", "#4653a3", "rgba(18,24,66,0.9)", "rgba(235,240,255,0.92)", "rgba(50,70,140,0.9)", "rgba(44,36,26,0.9)", "rgba(245,248,255,0.45)"], ["#0e4f4a", "#8fd0b0", "#fdeec1", "rgba(255,255,255,0.20)", "rgba(10,60,50,0.32)", "#1f6b52", "#2f8a68", "rgba(8,52,44,0.88)", "rgba(255,240,190,0.92)", "rgba(24,92,64,0.95)", "rgba(52,40,24,0.95)", "rgba(255,255,255,0.42)"], ["#4a2a63", "#d9a0c8", "#ffe9f4", "rgba(255,230,245,0.24)", "rgba(60,32,82,0.34)", "#5c3675", "#754a8e", "rgba(44,22,60,0.9)", "rgba(255,235,245,0.92)", "rgba(110,70,120,0.9)", "rgba(50,32,22,0.9)", "rgba(255,245,250,0.42)"], ["#3d2b1f", "#d9b38c", "#fff6e3", "rgba(255,240,215,0.24)", "rgba(60,42,28,0.36)", "#5a4030", "#71523c", "rgba(52,36,24,0.9)", "rgba(255,240,210,0.92)", "rgba(120,90,50,0.9)", "rgba(46,32,18,0.9)", "rgba(255,250,235,0.42)"], ]; fs.writeFileSync(path.join(OUT, n + ".svg"), campusScene(n, 640, 360, palettes[i], { water: i % 2 === 0 })); }); /* ---- 校徽 + 友链 logo ---- */ fs.writeFileSync(path.join(OUT, "logo-univ.svg"), emblem()); for (let i = 1; i <= 8; i++) { const hues = [210, 160, 20, 270, 120, 340, 40, 190]; const hue = hues[i - 1]; fs.writeFileSync(path.join(OUT, `logo-${i}.svg`), ` `); } /* ---- 最小 PDF(附件) ---- */ function miniPdf(title) { const text = `BT /F1 18 Tf 60 700 Td (${title}) Tj ET`; const content = `1 0 0 1 0 0 cm\n${text}`; const objs = [ "<< /Type /Catalog /Pages 2 0 R >>", "<< /Type /Pages /Kids [3 0 R] /Count 1 >>", "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>", `<< /Length ${content.length} >>\nstream\n${content}\nendstream`, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>", ]; let pdf = "%PDF-1.4\n"; const offsets = []; objs.forEach((o, i) => { offsets.push(pdf.length); pdf += `${i + 1} 0 obj\n${o}\nendobj\n`; }); const xref = pdf.length; pdf += `xref\n0 ${objs.length + 1}\n0000000000 65535 f \n`; offsets.forEach(off => { pdf += String(off).padStart(10, "0") + " 00000 n \n"; }); pdf += `trailer\n<< /Size ${objs.length + 1} /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF`; return Buffer.from(pdf, "latin1"); } fs.writeFileSync(path.join(OUT, "2026校历.pdf"), miniPdf("2026 QiulixiaoLi (seed)")); fs.writeFileSync(path.join(OUT, "运动会秩序册.pdf"), miniPdf("YunDongHui ZhiXuCe (seed)")); fs.writeFileSync(path.join(OUT, "迎新晚会节目单.pdf"), miniPdf("YingXin WanHui (seed)")); console.log("seed assets written to", OUT, ":", fs.readdirSync(OUT).length, "files");