root 651cc9e1fd D121: xiaohongshu-cover module v5 complete
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
2026-06-01 16:36:00 +08:00

317 lines
7.4 KiB
JavaScript
Raw Permalink 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.

/**
* ═══════════════════════════════════════════════════
* 海报模板 · 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>`
}