Files
hms/apps/web/src/pages/health/articleEditor/articleTemplates.ts
iven c716cc0f7b feat(web): 文章编辑器 — iPhone 15 Pro Max 高保真预览 + 丰富样式模板
- 手机预览按 iPhone 15 Pro Max 真实比例 (2.084:1) 重设计
- 钛金属渐变边框 + Dynamic Island + 前置摄像头 + 侧边按钮
- 背面摄像头模组微阴影暗示 + 多层投影深度效果
- iOS 17 风格状态栏 (信号/WiFi/电池 SVG 图标)
- 样式模板从 14 种扩展到 27 种
- 新增: 成功/危险/强调提示框、时间线、步骤流程、对比卡片、问答、进度条、引言卡片等
2026-05-11 02:41:30 +08:00

267 lines
19 KiB
TypeScript
Raw 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.
/**
* 文章编辑器样式模板数据
* 所有 HTML 片段使用内联样式,通过 wangEditor 自定义 styled-block 模块保留样式。
* 模板中使用 {{primary}} / {{primaryLight}} 占位符,由 applyTheme() 替换为实际颜色值。
*/
export interface StyleTemplate {
id: string;
name: string;
category: 'heading' | 'content' | 'block';
preview: string;
html: string;
}
export interface ColorTheme {
id: string;
name: string;
primary: string;
primaryLight: string;
}
export const COLOR_THEMES: ColorTheme[] = [
{ id: 'green', name: '清新绿', primary: '#16a34a', primaryLight: '#dcfce7' },
{ id: 'blue', name: '专业蓝', primary: '#2563eb', primaryLight: '#dbeafe' },
{ id: 'red', name: '暖橘红', primary: '#C4623A', primaryLight: '#F0DDD4' },
{ id: 'purple', name: '雅致紫', primary: '#7c3aed', primaryLight: '#ede9fe' },
{ id: 'amber', name: '琥珀金', primary: '#d97706', primaryLight: '#fef3c7' },
];
const W = 'data-w-e-type="styled-block"';
// ═══════════════════════════════════════
// 标题样式 (6 种)
// ═══════════════════════════════════════
export const HEADING_TEMPLATES: StyleTemplate[] = [
{
id: 'heading-classic',
name: '经典 · 左边框',
category: 'heading',
preview: '▎左边框标题',
html: `<div ${W} style="border-left: 4px solid {{primary}}; padding-left: 12px; font-size: 20px; font-weight: 700; color: #1a1a1a; margin: 24px 0 12px;">标题文本</div>`,
},
{
id: 'heading-underline',
name: '简约 · 下划线',
category: 'heading',
preview: '标题 ──────',
html: `<div ${W} style="border-bottom: 2px solid {{primary}}; padding-bottom: 8px; font-size: 20px; font-weight: 700; color: #1a1a1a; margin: 24px 0 12px; display: inline-block;">标题文本</div>`,
},
{
id: 'heading-badge',
name: '圆标 · 标签式',
category: 'heading',
preview: '■ 标签式标题',
html: `<div ${W} style="display: inline-block; background: {{primaryLight}}; color: {{primary}}; padding: 4px 14px; border-radius: 4px; font-size: 18px; font-weight: 700; margin: 24px 0 12px;">标题文本</div>`,
},
{
id: 'heading-centered',
name: '居中标题',
category: 'heading',
preview: '── 居中标题 ──',
html: `<div ${W} style="text-align: center; font-size: 20px; font-weight: 700; color: #1a1a1a; margin: 28px 0 12px;">标题文本</div>`,
},
{
id: 'heading-double-line',
name: '双线 · 上下框',
category: 'heading',
preview: '───────\n标题\n───────',
html: `<div ${W} style="text-align: center; margin: 24px 0 12px; padding: 10px 0; border-top: 2px solid {{primary}}; border-bottom: 2px solid {{primary}}; font-size: 20px; font-weight: 700; color: #1a1a1a;">标题文本</div>`,
},
{
id: 'heading-accent-bar',
name: '色带 · 顶部色条',
category: 'heading',
preview: '━━━\n标题文本',
html: `<div ${W} style="margin: 24px 0 12px; padding: 12px 0 0; border-top: 4px solid {{primary}}; font-size: 20px; font-weight: 700; color: #1a1a1a;">标题文本</div>`,
},
];
// ═══════════════════════════════════════
// 内容模板 (13 种)
// ═══════════════════════════════════════
export const CONTENT_TEMPLATES: StyleTemplate[] = [
{
id: 'blockquote',
name: '引用框',
category: 'content',
preview: '▎引用文字...',
html: `<div ${W} style="border-left: 3px solid {{primary}}; padding: 12px 16px; margin: 16px 0; background: #f9fafb; border-radius: 0 8px 8px 0; font-size: 15px; line-height: 1.8; color: #5a554f; font-style: italic;">引用内容请在此处编辑</div>`,
},
{
id: 'tip-warning',
name: '提示框 · 警告',
category: 'content',
preview: '⚠ 注意事项',
html: `<div ${W} style="background: #fffbeb; border: 1px solid #fde68a; border-radius: 8px; padding: 14px 16px; margin: 16px 0; font-size: 15px; line-height: 1.8; color: #92400e;"><strong>⚠ 注意事项:</strong>请在此处编辑警告内容。</div>`,
},
{
id: 'tip-info',
name: '提示框 · 信息',
category: 'content',
preview: ' 补充说明',
html: `<div ${W} style="background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; padding: 14px 16px; margin: 16px 0; font-size: 15px; line-height: 1.8; color: #1e40af;"><strong> 补充说明:</strong>请在此处编辑信息内容。</div>`,
},
{
id: 'tip-success',
name: '提示框 · 正确',
category: 'content',
preview: '✓ 正确做法',
html: `<div ${W} style="background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 14px 16px; margin: 16px 0; font-size: 15px; line-height: 1.8; color: #166534;"><strong>✓ 正确做法:</strong>请在此处编辑推荐内容。</div>`,
},
{
id: 'tip-danger',
name: '提示框 · 危险',
category: 'content',
preview: '✕ 严禁事项',
html: `<div ${W} style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 14px 16px; margin: 16px 0; font-size: 15px; line-height: 1.8; color: #991b1b;"><strong>✕ 严禁事项:</strong>请在此处编辑危险警告内容。</div>`,
},
{
id: 'tip-primary',
name: '提示框 · 强调',
category: 'content',
preview: '★ 重点强调',
html: `<div ${W} style="background: {{primaryLight}}; border: 1px solid {{primary}}; border-radius: 8px; padding: 14px 16px; margin: 16px 0; font-size: 15px; line-height: 1.8; color: #1a1a1a;"><strong style="color: {{primary}};">★ 重点强调:</strong>请在此处编辑重点内容。</div>`,
},
{
id: 'list-ordered',
name: '有序列表',
category: 'content',
preview: '① ② ③',
html: `<div ${W} style="padding-left: 20px; margin: 16px 0; font-size: 16px; line-height: 2; color: #3a3a3c;"><div style="position: relative; padding-left: 8px; margin-bottom: 4px;"><span style="position: absolute; left: -20px; color: {{primary}}; font-weight: 600;">1.</span>第一项内容</div><div style="position: relative; padding-left: 8px; margin-bottom: 4px;"><span style="position: absolute; left: -20px; color: {{primary}}; font-weight: 600;">2.</span>第二项内容</div><div style="position: relative; padding-left: 8px; margin-bottom: 4px;"><span style="position: absolute; left: -20px; color: {{primary}}; font-weight: 600;">3.</span>第三项内容</div></div>`,
},
{
id: 'list-unordered',
name: '无序列表',
category: 'content',
preview: '• • •',
html: `<div ${W} style="padding-left: 20px; margin: 16px 0; font-size: 16px; line-height: 2; color: #3a3a3c;"><div style="position: relative; padding-left: 8px; margin-bottom: 4px;"><span style="position: absolute; left: -14px; color: {{primary}};">●</span>第一项内容</div><div style="position: relative; padding-left: 8px; margin-bottom: 4px;"><span style="position: absolute; left: -14px; color: {{primary}};">●</span>第二项内容</div><div style="position: relative; padding-left: 8px; margin-bottom: 4px;"><span style="position: absolute; left: -14px; color: {{primary}};">●</span>第三项内容</div></div>`,
},
{
id: 'card-image-text',
name: '图文卡片',
category: 'content',
preview: '[图] 文字说明',
html: `<div ${W} style="display: flex; gap: 12px; margin: 16px 0; padding: 12px; background: #f9fafb; border-radius: 8px; align-items: center;"><div style="width: 100px; height: 80px; background: #e5e7eb; border-radius: 6px; display: flex; align-items: center; justify-content: center; color: #9ca3af; font-size: 12px; flex-shrink: 0;">图片</div><div style="flex: 1; font-size: 14px; line-height: 1.8; color: #3a3a3c;">图文卡片的文字描述内容请在此处编辑。</div></div>`,
},
{
id: 'card-data',
name: '数据卡片',
category: 'content',
preview: '血压 120/80',
html: `<div ${W} style="display: flex; gap: 12px; margin: 16px 0; flex-wrap: wrap;"><div style="flex: 1; min-width: 120px; background: {{primaryLight}}; border-radius: 8px; padding: 14px; text-align: center;"><div style="font-size: 24px; font-weight: 700; color: {{primary}};">120/80</div><div style="font-size: 13px; color: #5a554f; margin-top: 4px;">血压 (mmHg)</div></div><div style="flex: 1; min-width: 120px; background: #f3f4f6; border-radius: 8px; padding: 14px; text-align: center;"><div style="font-size: 24px; font-weight: 700; color: #1a1a1a;">72</div><div style="font-size: 13px; color: #5a554f; margin-top: 4px;">心率 (bpm)</div></div></div>`,
},
{
id: 'timeline',
name: '时间线',
category: 'content',
preview: '●── 第一步\n●── 第二步',
html: `<div ${W} style="margin: 16px 0; padding-left: 20px; border-left: 2px solid {{primary}};"><div style="position: relative; padding: 0 0 20px 16px;"><div style="position: absolute; left: -27px; top: 2px; width: 12px; height: 12px; border-radius: 50%; background: {{primary}};"></div><div style="font-size: 14px; font-weight: 600; color: #1a1a1a; margin-bottom: 2px;">2024年1月</div><div style="font-size: 14px; color: #5a554f; line-height: 1.6;">第一阶段内容描述</div></div><div style="position: relative; padding: 0 0 20px 16px;"><div style="position: absolute; left: -27px; top: 2px; width: 12px; height: 12px; border-radius: 50%; background: {{primary}};"></div><div style="font-size: 14px; font-weight: 600; color: #1a1a1a; margin-bottom: 2px;">2024年3月</div><div style="font-size: 14px; color: #5a554f; line-height: 1.6;">第二阶段内容描述</div></div><div style="position: relative; padding: 0 0 0 16px;"><div style="position: absolute; left: -27px; top: 2px; width: 12px; height: 12px; border-radius: 50%; background: {{primary}};"></div><div style="font-size: 14px; font-weight: 600; color: #1a1a1a; margin-bottom: 2px;">2024年6月</div><div style="font-size: 14px; color: #5a554f; line-height: 1.6;">第三阶段内容描述</div></div></div>`,
},
{
id: 'steps',
name: '步骤流程',
category: 'content',
preview: '➊ → ➋ → ➌',
html: `<div ${W} style="margin: 16px 0;"><div style="display: flex; align-items: flex-start; margin-bottom: 16px;"><div style="width: 28px; height: 28px; background: {{primary}}; color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; flex-shrink: 0; margin-right: 12px;">1</div><div style="flex: 1; padding-top: 4px;"><div style="font-size: 15px; font-weight: 600; color: #1a1a1a; margin-bottom: 2px;">第一步标题</div><div style="font-size: 14px; color: #5a554f; line-height: 1.6;">步骤一的详细说明内容</div></div></div><div style="display: flex; align-items: flex-start; margin-bottom: 16px;"><div style="width: 28px; height: 28px; background: {{primary}}; color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; flex-shrink: 0; margin-right: 12px;">2</div><div style="flex: 1; padding-top: 4px;"><div style="font-size: 15px; font-weight: 600; color: #1a1a1a; margin-bottom: 2px;">第二步标题</div><div style="font-size: 14px; color: #5a554f; line-height: 1.6;">步骤二的详细说明内容</div></div></div><div style="display: flex; align-items: flex-start;"><div style="width: 28px; height: 28px; background: {{primary}}; color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; flex-shrink: 0; margin-right: 12px;">3</div><div style="flex: 1; padding-top: 4px;"><div style="font-size: 15px; font-weight: 600; color: #1a1a1a; margin-bottom: 2px;">第三步标题</div><div style="font-size: 14px; color: #5a554f; line-height: 1.6;">步骤三的详细说明内容</div></div></div></div>`,
},
{
id: 'comparison',
name: '对比卡片',
category: 'content',
preview: '✓ 推荐 ✕ 避免',
html: `<div ${W} style="display: flex; gap: 12px; margin: 16px 0; flex-wrap: wrap;"><div style="flex: 1; min-width: 140px; background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 14px;"><div style="font-size: 15px; font-weight: 700; color: #166534; margin-bottom: 8px;">✓ 推荐做法</div><div style="font-size: 14px; color: #166534; line-height: 1.8;">推荐内容第一项<br/>推荐内容第二项<br/>推荐内容第三项</div></div><div style="flex: 1; min-width: 140px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 14px;"><div style="font-size: 15px; font-weight: 700; color: #991b1b; margin-bottom: 8px;">✕ 应该避免</div><div style="font-size: 14px; color: #991b1b; line-height: 1.8;">避免内容第一项<br/>避免内容第二项<br/>避免内容第三项</div></div></div>`,
},
{
id: 'faq',
name: '问答卡片',
category: 'content',
preview: 'Q: 问题\nA: 回答',
html: `<div ${W} style="margin: 16px 0; background: #f9fafb; border-radius: 8px; overflow: hidden;"><div style="background: {{primaryLight}}; padding: 10px 16px; font-size: 15px; font-weight: 600; color: {{primary}};">Q这里填写常见问题</div><div style="padding: 12px 16px; font-size: 14px; line-height: 1.8; color: #3a3a3c;">A这里填写问题的详细回答内容帮助读者理解相关知识。</div></div>`,
},
{
id: 'highlight-text',
name: '重点高亮',
category: 'content',
preview: '▸ 高亮重点内容',
html: `<div ${W} style="margin: 16px 0; padding: 12px 16px; background: {{primaryLight}}; border-radius: 8px; font-size: 15px; line-height: 1.8; color: #1a1a1a; border-left: 4px solid {{primary}};"><strong>重点:</strong>这里是需要高亮强调的关键内容。</div>`,
},
{
id: 'key-value',
name: '键值对列表',
category: 'content',
preview: '指标: 数值',
html: `<div ${W} style="margin: 16px 0; background: #f9fafb; border-radius: 8px; overflow: hidden; font-size: 14px;"><div style="display: flex; padding: 10px 16px; border-bottom: 1px solid #e5e7eb;"><div style="width: 100px; color: #5a554f; flex-shrink: 0;">检查项目</div><div style="flex: 1; font-weight: 600; color: #1a1a1a;">检测结果</div></div><div style="display: flex; padding: 10px 16px; border-bottom: 1px solid #e5e7eb; background: #fff;"><div style="width: 100px; color: #5a554f; flex-shrink: 0;">血红蛋白</div><div style="flex: 1; font-weight: 600; color: {{primary}};">110 g/L</div></div><div style="display: flex; padding: 10px 16px; border-bottom: 1px solid #e5e7eb;"><div style="width: 100px; color: #5a554f; flex-shrink: 0;">血清白蛋白</div><div style="flex: 1; font-weight: 600; color: {{primary}};">38 g/L</div></div><div style="display: flex; padding: 10px 16px; background: #fff;"><div style="width: 100px; color: #5a554f; flex-shrink: 0;">血钾</div><div style="flex: 1; font-weight: 600; color: #1a1a1a;">4.2 mmol/L</div></div></div>`,
},
];
// ═══════════════════════════════════════
// 区块组件 (6 种)
// ═══════════════════════════════════════
export const BLOCK_TEMPLATES: StyleTemplate[] = [
{
id: 'divider',
name: '分割线 · 虚线',
category: 'block',
preview: '─ ─ ─ ─',
html: `<div ${W} style="border: none; border-top: 1px dashed #d1d5db; margin: 24px 0; height: 0;"></div>`,
},
{
id: 'divider-gradient',
name: '分割线 · 渐变',
category: 'block',
preview: '━━━━━━━━',
html: `<div ${W} style="border: none; height: 2px; margin: 24px 0; background: linear-gradient(90deg, transparent, {{primary}}, transparent);"></div>`,
},
{
id: 'section-header',
name: '章节编号',
category: 'block',
preview: '§ 带编号章节',
html: `<div ${W} style="margin: 24px 0 12px; display: flex; align-items: center; gap: 10px;"><span style="display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; background: {{primary}}; color: #fff; border-radius: 50%; font-size: 14px; font-weight: 700; flex-shrink: 0;">1</span><span style="font-size: 18px; font-weight: 700; color: #1a1a1a;">章节标题</span></div>`,
},
{
id: 'table',
name: '数据表格',
category: 'block',
preview: '⊞ 3×2 表格',
html: `<div ${W} style="display: grid; grid-template-columns: 1fr 1fr 1fr; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; margin: 16px 0; font-size: 14px;"><div style="background: {{primaryLight}}; padding: 10px 12px; font-weight: 600; color: {{primary}}; border-bottom: 1px solid #e5e7eb; border-right: 1px solid #e5e7eb;">项目</div><div style="background: {{primaryLight}}; padding: 10px 12px; font-weight: 600; color: {{primary}}; border-bottom: 1px solid #e5e7eb; border-right: 1px solid #e5e7eb;">数值</div><div style="background: {{primaryLight}}; padding: 10px 12px; font-weight: 600; color: {{primary}}; border-bottom: 1px solid #e5e7eb;">备注</div><div style="padding: 10px 12px; border-bottom: 1px solid #e5e7eb; border-right: 1px solid #e5e7eb;">收缩压</div><div style="padding: 10px 12px; border-bottom: 1px solid #e5e7eb; border-right: 1px solid #e5e7eb;">120 mmHg</div><div style="padding: 10px 12px; border-bottom: 1px solid #e5e7eb;">正常</div><div style="background: #f9fafb; padding: 10px 12px; border-right: 1px solid #e5e7eb;">舒张压</div><div style="background: #f9fafb; padding: 10px 12px; border-right: 1px solid #e5e7eb;">80 mmHg</div><div style="background: #f9fafb; padding: 10px 12px;">正常</div></div>`,
},
{
id: 'progress',
name: '进度指示',
category: 'block',
preview: '█████░░░ 65%',
html: `<div ${W} style="margin: 16px 0;"><div style="display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 6px;"><span style="color: #1a1a1a; font-weight: 600;">治疗进度</span><span style="color: {{primary}}; font-weight: 700;">65%</span></div><div style="background: #e5e7eb; border-radius: 99px; height: 8px; overflow: hidden;"><div style="background: {{primary}}; height: 100%; width: 65%; border-radius: 99px;"></div></div></div>`,
},
{
id: 'quote-card',
name: '引言卡片',
category: 'block',
preview: '「」引用名言',
html: `<div ${W} style="margin: 20px 0; padding: 20px; background: linear-gradient(135deg, {{primaryLight}}, #f9fafb); border-radius: 12px; position: relative;"><div style="font-size: 36px; color: {{primary}}; opacity: 0.3; line-height: 1; font-family: Georgia, serif;">"</div><div style="font-size: 15px; color: #3a3a3c; line-height: 1.8; margin-top: -8px; font-style: italic;">这里填写引用的名言或重要语句。</div><div style="margin-top: 10px; font-size: 13px; color: #5a554f; font-weight: 500;">—— 作者名</div></div>`,
},
];
/** 所有模板合并列表 */
export const ALL_TEMPLATES: StyleTemplate[] = [
...HEADING_TEMPLATES,
...CONTENT_TEMPLATES,
...BLOCK_TEMPLATES,
];
/** 将模板 HTML 中的颜色占位符替换为主题实际颜色值 */
export function applyTheme(html: string, theme: ColorTheme): string {
return html
.replaceAll('{{primary}}', theme.primary)
.replaceAll('{{primaryLight}}', theme.primaryLight);
}
/** 根据 ID 查找颜色主题 */
export function getColorTheme(themeId: string): ColorTheme {
return COLOR_THEMES.find((t) => t.id === themeId) ?? COLOR_THEMES[0];
}