MODULE-COVER-001: full package for external AI consumption - server.js + renderer.js + generate.js + config.js - templates/ (5 templates: xiaohongshu + dynamic + jike + poster + registry) - package.json + README.md + MODULE.hdlp - INDEX.hdlp + SYSTEM.hdlp (HLDP declarations) - Fonts: Noto Sans CJK SC priority (no tofu blocks) - No domain watermark - Updated module-registry.json - Copyright 2026-A-00037559
317 lines
7.4 KiB
JavaScript
317 lines
7.4 KiB
JavaScript
/**
|
||
* ═══════════════════════════════════════════════════
|
||
* 海报模板 · 1080×1920 (9:16)
|
||
* ═══════════════════════════════════════════════════
|
||
*
|
||
* 适用于:放假通知、收稿海报、活动公告等
|
||
*/
|
||
|
||
import { STYLES } from '../config.js'
|
||
|
||
const W = STYLES.sizes.poster.width
|
||
const H = STYLES.sizes.poster.height
|
||
|
||
|
||
/**
|
||
* 生成海报 HTML
|
||
* @param {object} data
|
||
* @param {string} data.title - 大标题
|
||
* @param {string} data.subtitle - 副标题(可选)
|
||
* @param {string} data.body - 正文内容
|
||
* @param {string[]} data.details - 详情列表(如时间/地点/要求)
|
||
* @param {string} data.cta - 行动号召(如「欢迎投稿」)
|
||
* @param {string} data.footer - 底部信息(如主办方/联系方式)
|
||
* @param {string} data.tag - 角标标签(如「通知」)
|
||
* @param {'default'|'notice'|'recruit'} data.style - 海报风格
|
||
*/
|
||
export function posterCard(data) {
|
||
const {
|
||
title = '',
|
||
subtitle = '',
|
||
body = '',
|
||
details = [],
|
||
cta = '',
|
||
footer = '',
|
||
tag = '',
|
||
style = 'default',
|
||
} = data
|
||
|
||
const C = STYLES.colors
|
||
const F = STYLES.fonts
|
||
const T = STYLES.typography
|
||
|
||
// 详情列表渲染
|
||
const detailsHtml = details.map((d, i) => {
|
||
// 支持 key: value 格式
|
||
const colonIdx = d.indexOf(':')
|
||
if (colonIdx > 0 && colonIdx < 20) {
|
||
const key = d.slice(0, colonIdx)
|
||
const val = d.slice(colonIdx + 1)
|
||
return `
|
||
<div class="detail-item">
|
||
<span class="detail-key">${key}</span>
|
||
<span class="detail-colon">:</span>
|
||
<span class="detail-val">${val}</span>
|
||
</div>`
|
||
}
|
||
return `<div class="detail-item">${d}</div>`
|
||
}).join('\n')
|
||
|
||
return `<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700;900&family=Noto+Sans+SC:wght@300;400;500;700&display=swap');
|
||
|
||
body {
|
||
width: ${W}px;
|
||
height: ${H}px;
|
||
overflow: hidden;
|
||
background: ${C.bg};
|
||
font-family: ${F.body};
|
||
display: flex;
|
||
flex-direction: column;
|
||
position: relative;
|
||
}
|
||
|
||
/* ── 背景装饰 ── */
|
||
.bg-accent {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
width: 300px;
|
||
height: 300px;
|
||
background: radial-gradient(circle, ${C.accent}08 0%, transparent 70%);
|
||
}
|
||
.bg-accent-bl {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 400px;
|
||
height: 400px;
|
||
background: radial-gradient(circle, ${C.highlight}06 0%, transparent 70%);
|
||
}
|
||
|
||
.container {
|
||
flex: 1;
|
||
padding: 72px 64px 48px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* ── 标签 ── */
|
||
.tag {
|
||
display: inline-block;
|
||
padding: 6px 18px;
|
||
border: 1px solid ${C.accent};
|
||
color: ${C.accent};
|
||
font-size: ${T.captionSize}px;
|
||
border-radius: 4px;
|
||
margin-bottom: 32px;
|
||
align-self: flex-start;
|
||
letter-spacing: 3px;
|
||
}
|
||
|
||
/* ── 标题区 ── */
|
||
.title-section {
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
.title {
|
||
font-family: ${F.title};
|
||
font-size: 64px;
|
||
font-weight: 900;
|
||
line-height: 1.25;
|
||
color: ${C.primary};
|
||
letter-spacing: 4px;
|
||
}
|
||
|
||
.title-accent {
|
||
color: ${C.highlight};
|
||
}
|
||
|
||
.subtitle {
|
||
font-family: ${F.title};
|
||
font-size: ${T.subtitleSize}px;
|
||
font-weight: 300;
|
||
color: ${C.textMuted};
|
||
margin-top: 12px;
|
||
letter-spacing: 6px;
|
||
}
|
||
|
||
/* ── 分割线 ── */
|
||
.divider {
|
||
width: 80px;
|
||
height: 3px;
|
||
background: linear-gradient(90deg, ${C.gold}, ${C.highlight});
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
/* ── 正文 ── */
|
||
.body {
|
||
font-size: ${T.bodySize}px;
|
||
line-height: ${T.lineHeight};
|
||
color: ${C.text};
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.body p {
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
/* ── 详情列表 ── */
|
||
.details {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
margin-bottom: 32px;
|
||
padding: 32px;
|
||
background: ${C.warm}30;
|
||
border-radius: 8px;
|
||
border-left: 4px solid ${C.accent};
|
||
}
|
||
|
||
.detail-item {
|
||
font-size: ${T.bodySize}px;
|
||
line-height: 1.5;
|
||
color: ${C.text};
|
||
}
|
||
|
||
.detail-key {
|
||
font-weight: 600;
|
||
color: ${C.accent};
|
||
}
|
||
.detail-colon { color: ${C.textMuted}; }
|
||
.detail-val { color: ${C.text}; }
|
||
|
||
/* ── CTA ── */
|
||
.cta-section {
|
||
text-align: center;
|
||
padding: 24px 0;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.cta {
|
||
display: inline-block;
|
||
padding: 16px 48px;
|
||
background: linear-gradient(135deg, ${C.accent}, ${C.primary});
|
||
color: ${C.textLight};
|
||
font-size: ${T.subtitleSize}px;
|
||
font-weight: 600;
|
||
border-radius: 8px;
|
||
letter-spacing: 4px;
|
||
}
|
||
|
||
/* ── 底部 ── */
|
||
.footer {
|
||
text-align: center;
|
||
padding-top: 16px;
|
||
border-top: 1px solid ${C.divider};
|
||
font-size: ${T.captionSize}px;
|
||
color: ${C.textMuted};
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.brand-line {
|
||
opacity: ${STYLES.brand.opacity};
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* ── 通知风格:更正式 ── */
|
||
.notice-title {
|
||
font-family: ${F.title};
|
||
font-size: 56px;
|
||
font-weight: 700;
|
||
text-align: center;
|
||
letter-spacing: 8px;
|
||
color: ${C.primary};
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.notice-sub {
|
||
text-align: center;
|
||
font-size: ${T.smallSize}px;
|
||
color: ${C.textMuted};
|
||
letter-spacing: 4px;
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
/* ── 收稿风格:更有活力 ── */
|
||
.recruit-badge {
|
||
display: inline-block;
|
||
padding: 8px 24px;
|
||
background: ${C.highlight};
|
||
color: white;
|
||
font-size: ${T.smallSize}px;
|
||
border-radius: 4px;
|
||
letter-spacing: 2px;
|
||
margin-bottom: 20px;
|
||
align-self: center;
|
||
}
|
||
|
||
.recruit-title {
|
||
font-family: ${F.title};
|
||
font-size: 60px;
|
||
font-weight: 900;
|
||
text-align: center;
|
||
line-height: 1.3;
|
||
color: ${C.primary};
|
||
letter-spacing: 3px;
|
||
margin-bottom: 8px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="bg-accent"></div>
|
||
<div class="bg-accent-bl"></div>
|
||
|
||
<div class="container">
|
||
|
||
${style === 'notice' ? `
|
||
${tag ? `<div class="tag" style="align-self:center;">${tag}</div>` : ''}
|
||
<div class="notice-title">${title}</div>
|
||
${subtitle ? `<div class="notice-sub">${subtitle}</div>` : ''}
|
||
<div class="divider" style="align-self:center;"></div>
|
||
` : style === 'recruit' ? `
|
||
<div class="recruit-badge">${tag || '征稿启事'}</div>
|
||
<div class="recruit-title">${title}</div>
|
||
${subtitle ? `<div style="text-align:center;font-size:${T.smallSize}px;color:${C.textMuted};letter-spacing:2px;margin-bottom:24px;">${subtitle}</div>` : ''}
|
||
` : `
|
||
${tag ? `<div class="tag">${tag}</div>` : ''}
|
||
<div class="title-section">
|
||
<div class="title">${title}</div>
|
||
${subtitle ? `<div class="subtitle">${subtitle}</div>` : ''}
|
||
</div>
|
||
<div class="divider"></div>
|
||
`}
|
||
|
||
${body ? `<div class="body"><p>${body}</p></div>` : ''}
|
||
|
||
${details.length > 0 ? `<div class="details">${detailsHtml}</div>` : ''}
|
||
|
||
<div style="flex:1;"></div>
|
||
|
||
${cta ? `
|
||
<div class="cta-section">
|
||
<div class="cta">${cta}</div>
|
||
</div>
|
||
` : ''}
|
||
|
||
<div class="footer">
|
||
${footer ? `<div>${footer}</div>` : ''}
|
||
<div class="brand-line">${STYLES.brand.text}</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</body>
|
||
</html>`
|
||
}
|