193 lines
4.6 KiB
JavaScript
Raw Normal View History

/**
*
* 万能动态模板 · 自适应任何内容
*
*
* 不是固定类型是根据冰朔的文字内容现场设计
*/
import { STYLES } from '../config.js'
/**
* 万能动态卡片
* @param {object} data
* @param {string} data.title - 提炼的标题
* @param {string} data.body - 正文
* @param {string[]} data.lines - 原文行数组
* @param {string} data.type - 内容类型analyzeText 的结果
*/
export function dynamicCard(data) {
const { title, body, lines = [], type = 'general' } = data
const C = STYLES.colors
const F = STYLES.fonts
const T = STYLES.typography
const W = 1080
const H = 1080
// 动态计算字号:内容越多字越小
const totalChars = (title + body).length
const titleSize = totalChars < 20 ? 52 : totalChars < 50 ? 44 : 36
const bodySize = totalChars < 50 ? 32 : totalChars < 150 ? 26 : 22
// 检测有没有列表
const hasList = lines.some(l => /^[-·*]\s/.test(l.trim()))
// 检测是不是一句话
const isSingleLine = lines.length <= 2 && totalChars < 40
/* ── 生成内容 HTML ── */
let contentHtml = ''
if (isSingleLine) {
/* 一句话 · 大字居中 */
contentHtml = `
<div class="single-line">${escapeHtml(title)}</div>
`
} else if (hasList) {
/* 列表内容 */
const items = lines
.filter(l => /^[-·*]\s/.test(l.trim()))
.map(l => `<li>${escapeHtml(l.replace(/^[-•·*]\s*/, ''))}</li>`)
.join('\n')
contentHtml = `
<div class="title" style="font-size:${titleSize}px">${escapeHtml(title)}</div>
<ul class="dynamic-list">${items}</ul>
`
} else {
/* 一般段落 */
const paragraphs = lines
.filter(Boolean)
.map(l => `<p>${escapeHtml(l)}</p>`)
.join('\n')
contentHtml = `
<div class="title" style="font-size:${titleSize}px">${escapeHtml(title)}</div>
<div class="body-text" style="font-size:${bodySize}px">${paragraphs}</div>
`
}
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&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;
position: relative;
}
/* 动态背景装饰 */
.bg-circle {
position: absolute;
border-radius: 50%;
opacity: 0.04;
}
.bg-c1 {
width: 400px; height: 400px;
background: ${C.accent};
top: -100px; right: -100px;
}
.bg-c2 {
width: 250px; height: 250px;
background: ${C.highlight};
bottom: -60px; left: -60px;
}
.container {
flex: 1;
padding: 56px 60px;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
z-index: 1;
}
.single-line {
font-family: ${F.title};
font-size: ${titleSize}px;
font-weight: 600;
line-height: 1.5;
color: ${C.primary};
text-align: center;
letter-spacing: 3px;
}
.title {
font-family: ${F.title};
font-weight: 700;
line-height: 1.35;
color: ${C.primary};
margin-bottom: 24px;
letter-spacing: 1px;
}
.body-text {
line-height: 1.7;
color: ${C.text};
}
.body-text p {
margin-bottom: 10px;
}
.dynamic-list {
list-style: none;
padding: 0;
}
.dynamic-list li {
font-size: ${bodySize}px;
line-height: 1.5;
color: ${C.text};
padding: 12px 20px;
margin-bottom: 8px;
background: ${C.warm}40;
border-radius: 8px;
border-left: 3px solid ${C.accent};
}
/* 底部品牌 */
.brand-bar {
position: absolute;
bottom: 24px;
left: 0;
right: 0;
text-align: center;
font-size: 14px;
color: ${C.textMuted};
opacity: ${STYLES.brand.opacity};
z-index: 1;
}
</style>
</head>
<body>
<div class="bg-circle bg-c1"></div>
<div class="bg-circle bg-c2"></div>
<div class="container">
${contentHtml}
</div>
<div class="brand-bar">${STYLES.brand.text}</div>
</body>
</html>`
}
function escapeHtml(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
}