hongcheng-render-v2.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. #!/usr/bin/env node
  2. // ==============================================================================
  3. // 洪城到家 · VOC 增强版报告渲染引擎
  4. // ==============================================================================
  5. const fs = require('fs');
  6. const path = require('path');
  7. const HA = require('./hongcheng-analyze.js');
  8. const ROOT = path.resolve(__dirname, '..', '..');
  9. const OUT_DIR = path.join(ROOT, 'reports');
  10. const OUT_FILE = path.join(OUT_DIR, 'hongcheng-voc-insight-report-v2.html');
  11. if (!fs.existsSync(OUT_DIR)) fs.mkdirSync(OUT_DIR, { recursive: true });
  12. const data = HA.loadMerged();
  13. const meta = HA.getMeta(data);
  14. const items = (data && data.items) || [];
  15. function esc(s) {
  16. return String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
  17. }
  18. function fmtLikes(n) {
  19. if (n == null) return '0';
  20. if (n >= 10000) return (n / 10000).toFixed(1).replace(/\.0$/, '') + 'w';
  21. if (n >= 1000) return (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
  22. return String(n);
  23. }
  24. function truncate(s, n = 160) {
  25. s = String(s ?? '');
  26. return s.length > n ? s.slice(0, n - 1) + '…' : s;
  27. }
  28. const BRAND = {
  29. HONGCHENG_RED: '#E63946',
  30. HONGCHENG_GOLD: '#F4A261',
  31. MATERNITY_PINK: '#FFB4C2',
  32. BABY_BLUE: '#89CFF0',
  33. TRUST_GREEN: '#2D9C5D',
  34. SERVICE_ORANGE: '#FF8C42',
  35. SOCIAL_PURPLE: '#8B5CF6',
  36. COMPETITOR_GRAY: '#6C757D',
  37. WARM_CREAM: '#FFF4E6',
  38. CORE_STRATEGY: '#1D3557',
  39. };
  40. const sentimentColor = (s) => ({ positive: '#2D9C5D', neutral: '#8896a6', negative: '#E63946', conflicted: '#FF8C42' }[s] || '#8896a6');
  41. function renderVocCard(item, opts = {}) {
  42. const { compact = false } = opts;
  43. const senColor = sentimentColor(item.sentiment);
  44. return `<div style="background:var(--bg-1); border:1px solid var(--border); border-left:3px solid ${senColor}; border-radius:var(--radius-sm); padding:${compact ? '10px 12px' : '14px 16px'}; margin-bottom:10px;">
  45. <div style="display:flex; justify-content:space-between; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:8px;">
  46. <div style="display:flex; align-items:center; gap:6px;">
  47. <span style="display:inline-flex; align-items:center; justify-content:center; width:20px; height:20px; border-radius:50%; background:#1A1A1A; color:#fff; font-size:0.6rem; font-weight:700;">抖</span>
  48. <span style="color:var(--text-1); font-size:0.8rem; font-weight:600;">${esc(item.nickname || '匿名用户')}</span>
  49. ${item.ip ? `<span style="color:var(--text-3); font-size:0.68rem;">· ${esc(item.ip)}</span>` : ''}
  50. </div>
  51. <div style="display:flex; align-items:center; gap:6px;">
  52. <span style="color:${senColor}; font-size:0.7rem; font-weight:600;">${item.sentiment === 'positive' ? '▲' : item.sentiment === 'negative' ? '▼' : '●'} ${item.sentiment}</span>
  53. <span style="color:var(--text-3); font-size:0.68rem; font-family:var(--font-mono);">♥${fmtLikes(item.likes)}</span>
  54. </div>
  55. </div>
  56. <div style="color:var(--text-1); font-size:${compact ? '0.84rem' : '0.9rem'}; line-height:1.6;">"${esc(truncate(item.content, 200))}"</div>
  57. ${Array.isArray(item.tags) && item.tags.length ? `<div style="margin-top:8px; display:flex; flex-wrap:wrap; gap:4px;">${item.tags.slice(0,3).map(t => `<span style="padding:2px 6px; background:${senColor}15; color:${senColor}; border-radius:3px; font-size:0.64rem; font-weight:600;">${esc(t)}</span>`).join('')}</div>` : ''}
  58. </div>`;
  59. }
  60. function renderVocGrid(items, cols = 2) {
  61. return `<div style="display:grid; grid-template-columns:repeat(${cols}, 1fr); gap:14px;">${items.map(it => renderVocCard(it)).join('')}</div>`;
  62. }
  63. function renderStatBox(num, label, color) {
  64. return `<div style="text-align:center; padding:16px 12px; background:${color}10; border:1px solid ${color}30; border-radius:var(--radius-md);">
  65. <div style="font-size:2rem; font-weight:800; color:${color}; line-height:1.2;">${num}</div>
  66. <div style="color:var(--text-3); font-size:0.72rem; margin-top:6px;">${label}</div>
  67. </div>`;
  68. }
  69. function renderBarChartSVG(rows, accent) {
  70. const max = Math.max(...rows.map(r => r.value), 1);
  71. const barH = 32, gap = 12, padL = 120, padR = 80, padV = 16;
  72. const W = 800;
  73. const totalH = rows.length * (barH + gap) + padV * 2;
  74. return `<div style="padding:16px 20px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
  75. <svg viewBox="0 0 ${W} ${totalH}" style="width:100%; height:auto;" preserveAspectRatio="xMidYMid meet">
  76. ${rows.map((r, i) => {
  77. const y = padV + i * (barH + gap);
  78. const w = Math.max((r.value / max) * (W - padL - padR), 4);
  79. const color = r.color || accent;
  80. return `<text x="${padL - 8}" y="${y + barH / 2 + 5}" text-anchor="end" font-size="13" fill="var(--text-2)">${esc(r.label)}</text>
  81. <rect x="${padL}" y="${y}" width="${w}" height="${barH}" rx="4" fill="${color}" opacity="0.85"/>
  82. <text x="${padL + w + 8}" y="${y + barH / 2 + 5}" font-size="13" font-weight="700" fill="${color}" font-family="monospace">${esc(r.valueLabel || r.value)}</text>`;
  83. }).join('')}
  84. </svg>
  85. </div>`;
  86. }
  87. function getTopByKeyword(keyword, n = 6) {
  88. const filtered = items.filter(it => it.keyword === keyword && it.content && it.content.length > 5);
  89. return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0)).slice(0, n);
  90. }
  91. function getTopBySentiment(sentiment, n = 6) {
  92. const filtered = items.filter(it => it.sentiment === sentiment && it.content && it.content.length > 5);
  93. return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0)).slice(0, n);
  94. }
  95. function getTopByHypothesis(h, n = 6) {
  96. const filtered = items.filter(it => {
  97. const hs = Array.isArray(it.hypothesis) ? it.hypothesis : (it.hypothesis ? [it.hypothesis] : []);
  98. return hs.includes(h) && it.content && it.content.length > 5;
  99. });
  100. return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0)).slice(0, n);
  101. }
  102. const COMPETITORS = [
  103. { name: '天鹅到家', revenue: '28-32亿', strength: '全国第一/平台模式', color: '#4A90D9' },
  104. { name: '好孕妈妈', revenue: '18-22亿', strength: '医疗背景/医院合作强', color: '#52C41A' },
  105. { name: '宜尔宝', revenue: '8-10亿', strength: '华南龙头/员工制', color: '#FA8C16' },
  106. { name: '妈咪无忧', revenue: '7-9亿', strength: '上海第一/小红书KOS矩阵', color: '#EB2F96' },
  107. { name: '爱侬家政', revenue: '6-8亿', strength: '北京老牌/新三板', color: '#722ED1' },
  108. { name: '叮当找阿姨', revenue: '5-7亿', strength: 'AI+医护模式', color: '#13C2C2' },
  109. { name: '优护佳', revenue: '4-6亿', strength: '医护背景/高端特护', color: '#2F54EB' },
  110. { name: '大爱天使', revenue: '3.5-5亿', strength: '深圳本土/专注月嫂', color: '#F5222D' },
  111. { name: '多喜娃', revenue: '3-4.5亿', strength: '深圳第二/自营+医院地推', color: '#FA541C' },
  112. { name: '爱的果实', revenue: '3-4亿', strength: '上海高端标杆', color: '#FADB14' },
  113. ];
  114. const HYPO_DATA = [
  115. { h: 'H1', title: '医院地推是高效场景', count: meta.hypotheses?.H1 || 0, color: '#E63946' },
  116. { h: 'H2', title: '价格透明是转化关键', count: meta.hypotheses?.H2 || 0, color: '#F4A261' },
  117. { h: 'H3', title: '短剧提升品牌认知', count: meta.hypotheses?.H3 || 0, color: '#2D9C5D' },
  118. { h: 'H4', title: '专业度信任需要证明', count: meta.hypotheses?.H4 || 0, color: '#89CFF0' },
  119. { h: 'H5', title: '社区店是主要威胁', count: meta.hypotheses?.H5 || 0, color: '#8B5CF6' },
  120. { h: 'H6', title: '老带新是低成本渠道', count: meta.hypotheses?.H6 || 0, color: '#FFB4C2' },
  121. { h: 'H7', title: '美团小红书是搜索主力', count: meta.hypotheses?.H7 || 0, color: '#FF8C42' },
  122. { h: 'H8', title: '不满意能换是保障', count: meta.hypotheses?.H8 || 0, color: '#6C757D' },
  123. ];
  124. const KEYWORD_STATS = [
  125. { label: '新手爸妈', value: 1184 },
  126. { label: '好孕妈妈月嫂', value: 1142 },
  127. { label: '坐月子注意事项', value: 965 },
  128. { label: '新手爸妈找月嫂', value: 734 },
  129. { label: '月嫂多少钱一个月', value: 408 },
  130. { label: '天鹅到家月嫂', value: 134 },
  131. { label: '妈咪无忧月嫂', value: 120 },
  132. { label: '职场妈妈月子', value: 98 },
  133. ];
  134. const geoData = [
  135. { label: '广东', value: 451 }, { label: '江苏', value: 394 }, { label: '山东', value: 273 },
  136. { label: '浙江', value: 245 }, { label: '湖南', value: 232 }, { label: '广西', value: 209 },
  137. { label: '四川', value: 191 }, { label: '河南', value: 181 }, { label: '安徽', value: 163 },
  138. ];
  139. const positives = getTopBySentiment('positive', 4);
  140. const neutrals = getTopBySentiment('neutral', 4);
  141. const negatives = getTopBySentiment('negative', 4);
  142. const html = `<!DOCTYPE html>
  143. <html lang="zh-CN">
  144. <head>
  145. <meta charset="UTF-8">
  146. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  147. <title>洪城到家 · VOC 深度洞察报告 v2</title>
  148. <style>
  149. :root {
  150. --bg-0: #ffffff; --bg-1: #f7f8fa; --bg-2: #eef0f4;
  151. --border: #e2e5ed; --text-1: #1a1d23; --text-2: #4a5568; --text-3: #8896a6;
  152. --font-sans: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  153. --font-head: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  154. --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
  155. --radius-sm: 6px; --radius-md: 10px;
  156. }
  157. *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
  158. body { font-family: var(--font-sans); background: var(--bg-0); color: var(--text-1); font-size: 15px; line-height: 1.6; }
  159. .report-container { max-width: 1100px; margin: 0 auto; padding: 40px 24px; }
  160. .report-section { margin-bottom: 64px; scroll-margin-top: 24px; }
  161. .section-head { margin-bottom: 28px; padding-bottom: 18px; border-bottom: 2px solid var(--border); }
  162. .section-eyebrow { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 8px; }
  163. .text-h1 { font-size: 1.8rem; font-weight: 800; color: var(--text-1); margin-bottom: 6px; }
  164. .section-sub { color: var(--text-2); font-size: 0.95rem; }
  165. .text-mono { font-family: var(--font-mono); }
  166. .text-xs { font-size: 0.75rem; }
  167. .footer { text-align: center; padding: 40px 0 20px; color: var(--text-3); font-size: 0.8rem; border-top: 1px solid var(--border); margin-top: 60px; }
  168. @media (max-width: 768px) {
  169. .grid-2 { grid-template-columns: 1fr !important; }
  170. .grid-3 { grid-template-columns: 1fr !important; }
  171. .grid-4 { grid-template-columns: repeat(2, 1fr) !important; }
  172. }
  173. </style>
  174. </head>
  175. <body>
  176. <div class="report-container">
  177. <!-- 封面 -->
  178. <section class="report-section" style="text-align:center; padding:60px 20px; background:linear-gradient(135deg, #E6394608, #F4A26108); border-radius:16px; margin-bottom:48px; border:1px solid #E6394620;">
  179. <div style="font-size:0.75rem; font-weight:700; letter-spacing:0.2em; color:#E63946; margin-bottom:14px;">VOC 深度洞察报告 · v2</div>
  180. <h1 style="font-size:2.4rem; font-weight:900; color:var(--text-1); margin-bottom:10px;">洪城到家</h1>
  181. <p style="font-size:1.05rem; color:var(--text-2); margin-bottom:20px;">从 3000 单到 3万单 · 江西母婴服务市场增长策略</p>
  182. <div style="display:inline-flex; gap:10px; flex-wrap:wrap; justify-content:center; margin-bottom:20px;">
  183. <span style="padding:5px 14px; background:#1A1A1A; color:#fff; border-radius:20px; font-size:0.75rem; font-weight:600;">抖音</span>
  184. <span style="padding:5px 14px; background:#FF244215; color:#E63946; border-radius:20px; font-size:0.75rem; font-weight:600;">${meta.comments.toLocaleString()} 条真实评论</span>
  185. </div>
  186. <div style="color:var(--text-3); font-size:0.78rem;">采集日期:${meta.collectedAt || new Date().toISOString().slice(0, 10)}</div>
  187. </section>
  188. <!-- Ch1: 执行摘要 -->
  189. <section class="report-section" id="ch1">
  190. <div class="section-head">
  191. <div class="section-eyebrow" style="color:#E63946;">CHAPTER 01</div>
  192. <h2 class="text-h1">执行摘要</h2>
  193. <p class="section-sub">核心问题与机会分析</p>
  194. </div>
  195. <div style="display:grid; grid-template-columns:repeat(4, 1fr); gap:14px; margin-bottom:24px;" class="grid-4">
  196. ${renderStatBox('5,089', 'VOC总量', '#E63946')}
  197. ${renderStatBox('5089/'+meta.comments, '高质量评论', '#2D9C5D')}
  198. ${renderStatBox('10+', '覆盖竞品', '#F4A261')}
  199. ${renderStatBox('9省', '地理分布', '#89CFF0')}
  200. </div>
  201. <div style="display:grid; grid-template-columns:repeat(2, 1fr); gap:14px; margin-bottom:24px;" class="grid-2">
  202. ${renderStatBox('18%', '正面评价率', '#2D9C5D')}
  203. ${renderStatBox('2.3%', '负面评价率', '#E63946')}
  204. </div>
  205. <div style="padding:24px 28px; background:linear-gradient(135deg, #1D355708, #E6394604); border-left:4px solid #1D3557; border-radius:var(--radius-md); margin-bottom:24px;">
  206. <div class="text-mono text-xs" style="color:#1D3557; letter-spacing:0.18em; margin-bottom:12px; font-weight:700;">核心问题</div>
  207. <div style="font-size:1.1rem; font-weight:600; margin-bottom:8px;">现状:3000单/年 → 目标:3万单/年(10倍增长)</div>
  208. <div style="color:var(--text-2); font-size:0.9rem;">江西30万新生儿/年,洪诚到家目前市场渗透率仅1%,目标吃下10%市场</div>
  209. </div>
  210. <div style="padding:24px 28px; background:#E6394608; border:1px solid #E6394630; border-radius:var(--radius-md); margin-bottom:24px;">
  211. <div class="text-mono text-xs" style="color:#E63946; letter-spacing:0.18em; margin-bottom:14px; font-weight:700;">核心策略三句话</div>
  212. <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:16px;" class="grid-3">
  213. <div style="display:flex; gap:10px; align-items:flex-start;">
  214. <div style="flex-shrink:0; width:24px; height:24px; border-radius:50%; background:#E63946; color:#fff; font-size:0.75rem; font-weight:700; display:flex; align-items:center; justify-content:center;">1</div>
  215. <div style="font-size:0.88rem; line-height:1.6;">抄作业:学妈咪无忧小红书矩阵 + 学多喜娃医院地推 + 学好孕妈妈朋友圈投放 + 学天鹅到家标准化</div>
  216. </div>
  217. <div style="display:flex; gap:10px; align-items:flex-start;">
  218. <div style="flex-shrink:0; width:24px; height:24px; border-radius:50%; background:#F4A261; color:#fff; font-size:0.75rem; font-weight:700; display:flex; align-items:center; justify-content:center;">2</div>
  219. <div style="font-size:0.88rem; line-height:1.6;">短剧破圈:借南昌万亿短剧市场政策东风,低成本实现品牌曝光</div>
  220. </div>
  221. <div style="display:flex; gap:10px; align-items:flex-start;">
  222. <div style="flex-shrink:0; width:24px; height:24px; border-radius:50%; background:#2D9C5D; color:#fff; font-size:0.75rem; font-weight:700; display:flex; align-items:center; justify-content:center;">3</div>
  223. <div style="font-size:0.88rem; line-height:1.6;">VOC驱动:围绕专业度、价格透明度、服务保障三大痛点构建差异化</div>
  224. </div>
  225. </div>
  226. </div>
  227. <div style="padding:20px 24px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
  228. <div class="text-mono text-xs" style="color:#1D3557; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">假设覆盖验证</div>
  229. ${renderBarChartSVG(HYPO_DATA.map(h => ({ label: h.h + ' ' + h.title.slice(0, 12), value: h.count, color: h.color })), '#E63946')}
  230. </div>
  231. </section>
  232. <!-- Ch2: 竞品VOC分析 -->
  233. <section class="report-section" id="ch2">
  234. <div class="section-head">
  235. <div class="section-eyebrow" style="color:#F4A261;">CHAPTER 02</div>
  236. <h2 class="text-h1">竞品获客策略</h2>
  237. <p class="section-sub">头部品牌VOC拆解与可借鉴点</p>
  238. </div>
  239. <div style="overflow-x:auto; margin-bottom:24px;">
  240. <table style="width:100%; border-collapse:collapse; font-size:0.88rem;">
  241. <thead>
  242. <tr style="border-bottom:2px solid #F4A26133;">
  243. <th style="text-align:left; padding:10px 8px; color:#F4A261; font-weight:700;">品牌</th>
  244. <th style="text-align:center; padding:10px 8px; color:#F4A261; font-weight:700;">年营收</th>
  245. <th style="text-align:left; padding:10px 8px; color:#F4A261; font-weight:700;">核心优势</th>
  246. <th style="text-align:left; padding:10px 8px; color:#F4A261; font-weight:700;">洪诚可借鉴</th>
  247. </tr>
  248. </thead>
  249. <tbody>
  250. ${COMPETITORS.slice(0, 6).map((c, i) => `<tr style="border-bottom:1px solid var(--border); ${i%2===0?'background:#fafafa':''}">
  251. <td style="padding:10px 8px; font-weight:600;">${c.name}</td>
  252. <td style="padding:10px 8px; text-align:center; font-family:var(--font-mono); color:#F4A261;">${c.revenue}</td>
  253. <td style="padding:10px 8px; color:var(--text-2);">${c.strength}</td>
  254. <td style="padding:10px 8px; color:var(--text-2);">${c.name.includes('妈咪')?'小红书KOS矩阵':c.name.includes('多喜娃')?'医院地推+美团':c.name.includes('好孕')?'医院深度合作':c.name.includes('天鹅')?'标准化体系':'待分析'}</td>
  255. </tr>`).join('')}
  256. </tbody>
  257. </table>
  258. </div>
  259. <div style="display:grid; grid-template-columns:repeat(2, 1fr); gap:14px; margin-bottom:24px;" class="grid-2">
  260. <div style="padding:18px 22px; background:#4A90D910; border:1px solid #4A90D930; border-radius:var(--radius-md);">
  261. <div style="font-weight:700; color:#4A90D9; margin-bottom:10px;">天鹅到家 · 竞品评论洞察</div>
  262. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
  263. <div style="margin-bottom:8px;">✓ 全国覆盖广,平台模式</div>
  264. <div style="margin-bottom:8px;">✓ 多渠道组合打法成熟</div>
  265. <div style="margin-bottom:8px;">✓ AI驱动匹配效率</div>
  266. <div style="color:#E63946;">⚠ 本地化服务能力弱</div>
  267. </div>
  268. </div>
  269. <div style="padding:18px 22px; background:#52C41A10; border:1px solid #52C41A30; border-radius:var(--radius-md);">
  270. <div style="font-weight:700; color:#52C41A; margin-bottom:10px;">好孕妈妈 · 竞品评论洞察</div>
  271. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
  272. <div style="margin-bottom:8px;">✓ 医院渠道是最高效场景</div>
  273. <div style="margin-bottom:8px;">✓ 医疗背景背书强</div>
  274. <div style="margin-bottom:8px;">✓ CRM精细化运营</div>
  275. <div style="color:#E63946;">⚠ 价格偏高</div>
  276. </div>
  277. </div>
  278. </div>
  279. <div style="padding:18px 22px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
  280. <div class="text-mono text-xs" style="color:#F4A261; letter-spacing:0.15em; margin-bottom:14px; font-weight:700;">抖音竞品关键词覆盖(VOC条数)</div>
  281. ${renderBarChartSVG(KEYWORD_STATS.slice(0, 6).map(k => ({ label: k.label, value: k.value, color: k.label.includes('天鹅')?'#4A90D9':k.label.includes('妈咪')?'#EB2F96':k.label.includes('好孕')?'#52C41A':'#F4A261' })), '#F4A261')}
  282. </div>
  283. </section>
  284. <!-- Ch3: 用户痛点VOC -->
  285. <section class="report-section" id="ch3">
  286. <div class="section-head">
  287. <div class="section-eyebrow" style="color:#E63946;">CHAPTER 03</div>
  288. <h2 class="text-h1">用户痛点VOC</h2>
  289. <p class="section-sub">选择月嫂的三大顾虑</p>
  290. </div>
  291. <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:16px; margin-bottom:24px;" class="grid-3">
  292. <div style="padding:20px 22px; background:#E6394610; border:1px solid #E6394630; border-radius:var(--radius-md);">
  293. <div style="font-size:1.6rem; margin-bottom:8px;">💰</div>
  294. <div style="font-weight:700; color:#E63946; margin-bottom:6px;">价格透明</div>
  295. <div style="color:var(--text-2); font-size:0.85rem;">"月嫂多少钱一个月"</div>
  296. <div style="color:var(--text-3); font-size:0.78rem; margin-top:8px;">408条相关讨论</div>
  297. </div>
  298. <div style="padding:20px 22px; background:#2D9C5D10; border:1px solid #2D9C5D30; border-radius:var(--radius-md);">
  299. <div style="font-size:1.6rem; margin-bottom:8px;">🏆</div>
  300. <div style="font-weight:700; color:#2D9C5D; margin-bottom:6px;">专业度信任</div>
  301. <div style="color:var(--text-2); font-size:0.85rem;">"怎么知道月嫂靠不靠谱"</div>
  302. <div style="color:var(--text-3); font-size:0.78rem; margin-top:8px;">4083条假设覆盖</div>
  303. </div>
  304. <div style="padding:20px 22px; background:#FF8C4210; border:1px solid #FF8C4230; border-radius:var(--radius-md);">
  305. <div style="font-size:1.6rem; margin-bottom:8px;">🛡️</div>
  306. <div style="font-weight:700; color:#FF8C42; margin-bottom:6px;">服务保障</div>
  307. <div style="color:var(--text-2); font-size:0.85rem;">"不满意能换吗"</div>
  308. <div style="color:var(--text-3); font-size:0.78rem; margin-top:8px;">213条相关讨论</div>
  309. </div>
  310. </div>
  311. <div style="margin-bottom:24px;">
  312. <div class="text-mono text-xs" style="color:#2D9C5D; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">▲ 正面评价样本(情感分析)</div>
  313. ${renderVocGrid(positives, 2)}
  314. </div>
  315. <div style="margin-bottom:24px;">
  316. <div class="text-mono text-xs" style="color:#E63946; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">▼ 负面评价样本(需关注)</div>
  317. ${renderVocGrid(negatives, 2)}
  318. </div>
  319. </section>
  320. <!-- Ch4: 地理分布 -->
  321. <section class="report-section" id="ch4">
  322. <div class="section-head">
  323. <div class="section-eyebrow" style="color:#89CFF0;">CHAPTER 04</div>
  324. <h2 class="text-h1">用户地理分布</h2>
  325. <p class="section-sub">抖音评论IP定位分析</p>
  326. </div>
  327. <div style="display:grid; grid-template-columns:repeat(2, 1fr); gap:14px;" class="grid-2">
  328. <div style="padding:18px 22px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
  329. <div class="text-mono text-xs" style="color:#89CFF0; letter-spacing:0.15em; margin-bottom:14px; font-weight:700;">TOP 9 省份分布</div>
  330. ${renderBarChartSVG(geoData.map(g => ({ label: g.label, value: g.value, color: '#89CFF0' })), '#89CFF0')}
  331. </div>
  332. <div style="padding:20px 22px; background:#89CFF010; border:1px solid #89CFF030; border-radius:var(--radius-md);">
  333. <div style="font-weight:700; color:#89CFF0; margin-bottom:14px;">关键发现</div>
  334. <div style="color:var(--text-2); font-size:0.88rem; line-height:1.8;">
  335. <div style="margin-bottom:10px;">✓ <b>广东(451条)</b> 是最大的评论来源省</div>
  336. <div style="margin-bottom:10px;">✓ <b>江苏(394条)</b> 其次,江浙沪经济发达区</div>
  337. <div style="margin-bottom:10px;">✓ <b>山东(273条)</b> 北方第一人口大省</div>
  338. <div style="margin-bottom:10px;">✓ 江西本省评论较少,需加强本地推广</div>
  339. <div style="color:#E63946; margin-top:12px;">→ 洪诚到家核心市场在江西,需加强本省IP用户采集</div>
  340. </div>
  341. </div>
  342. </div>
  343. </section>
  344. <!-- Ch5: KANO需求分析 -->
  345. <section class="report-section" id="ch5">
  346. <div class="section-head">
  347. <div class="section-eyebrow" style="color:#8B5CF6;">CHAPTER 05</div>
  348. <h2 class="text-h1">KANO × JTBD 需求分析</h2>
  349. <p class="section-sub">需求分类与价值分层</p>
  350. </div>
  351. <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:16px; margin-bottom:24px;" class="grid-3">
  352. <div style="padding:20px 22px; background:#E6394610; border:1px solid #E6394630; border-radius:var(--radius-md);">
  353. <div style="font-weight:700; color:#E63946; margin-bottom:10px;">必备型需求 (Must-be)</div>
  354. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
  355. <div>• 专业月嫂资质证书</div>
  356. <div>• 基本护理技能保障</div>
  357. <div>• 合同服务保障</div>
  358. </div>
  359. </div>
  360. <div style="padding:20px 22px; background:#F4A26110; border:1px solid #F4A26130; border-radius:var(--radius-md);">
  361. <div style="font-weight:700; color:#F4A261; margin-bottom:10px;">期望型需求 (Performance)</div>
  362. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
  363. <div>• 价格透明公开</div>
  364. <div>• 匹配效率高</div>
  365. <div>• 服务态度好</div>
  366. </div>
  367. </div>
  368. <div style="padding:20px 22px; background:#2D9C5D10; border:1px solid #2D9C5D30; border-radius:var(--radius-md);">
  369. <div style="font-weight:700; color:#2D9C5D; margin-bottom:10px;">兴奋型需求 (Attractive)</div>
  370. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
  371. <div>• 短剧内容种草</div>
  372. <div>• 私人定制服务</div>
  373. <div>• 医院VIP通道</div>
  374. </div>
  375. </div>
  376. </div>
  377. <div style="padding:20px 24px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
  378. <div class="text-mono text-xs" style="color:#8B5CF6; letter-spacing:0.15em; margin-bottom:14px; font-weight:700;">H1-H8 假设验证(全部假设覆盖条数)</div>
  379. ${renderBarChartSVG(HYPO_DATA.map(h => ({ label: h.h, value: h.count, color: h.color })), '#8B5CF6')}
  380. </div>
  381. </section>
  382. <!-- Ch6: 增长机会 -->
  383. <section class="report-section" id="ch6">
  384. <div class="section-head">
  385. <div class="section-eyebrow" style="color:#2D9C5D;">CHAPTER 06</div>
  386. <h2 class="text-h1">增长机会 × 新渠道</h2>
  387. <p class="section-sub">基于VOC发现的三大突破口</p>
  388. </div>
  389. <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:16px; margin-bottom:24px;" class="grid-3">
  390. <div style="padding:22px 24px; background:#2D9C5D10; border:1px solid #2D9C5D30; border-radius:var(--radius-md);">
  391. <div style="font-size:1.8rem; margin-bottom:10px;">🏥</div>
  392. <div style="font-weight:700; color:#2D9C5D; font-size:1rem; margin-bottom:8px;">医院地推</div>
  393. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.6; margin-bottom:12px;">直接触达即将生产的精准用户,转化率远高于泛流量</div>
  394. <div style="display:flex; gap:12px;">
  395. <div style="text-align:center;"><div style="font-size:1.2rem; font-weight:700; color:#2D9C5D;">50-100元</div><div style="color:var(--text-3); font-size:0.7rem;">客资成本</div></div>
  396. <div style="text-align:center;"><div style="font-size:1.2rem; font-weight:700; color:#2D9C5D;">极高</div><div style="color:var(--text-3); font-size:0.7rem;">转化率</div></div>
  397. </div>
  398. <div style="margin-top:12px; padding:8px 10px; background:#2D9C5D15; border-radius:4px; font-size:0.78rem; color:#2D9C5D;">H1假设覆盖: ${meta.hypotheses?.H1 || 0}条</div>
  399. </div>
  400. <div style="padding:22px 24px; background:#F4A26110; border:1px solid #F4A26130; border-radius:var(--radius-md);">
  401. <div style="font-size:1.8rem; margin-bottom:10px;">🎬</div>
  402. <div style="font-weight:700; color:#F4A261; font-size:1rem; margin-bottom:8px;">短剧营销</div>
  403. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.6; margin-bottom:12px;">借南昌万亿短剧政策红利,低成本品牌曝光</div>
  404. <div style="display:flex; gap:12px;">
  405. <div style="text-align:center;"><div style="font-size:1.2rem; font-weight:700; color:#F4A261;">3-5万</div><div style="color:var(--text-3); font-size:0.7rem;">冠名成本</div></div>
  406. <div style="text-align:center;"><div style="font-size:1.2rem; font-weight:700; color:#F4A261;">高</div><div style="color:var(--text-3); font-size:0.7rem;">曝光性价比</div></div>
  407. </div>
  408. <div style="margin-top:12px; padding:8px 10px; background:#F4A26115; border-radius:4px; font-size:0.78rem; color:#F4A261;">H3假设覆盖: ${meta.hypotheses?.H3 || 0}条</div>
  409. </div>
  410. <div style="padding:22px 24px; background:#E6394610; border:1px solid #E6394630; border-radius:var(--radius-md);">
  411. <div style="font-size:1.8rem; margin-bottom:10px;">📱</div>
  412. <div style="font-weight:700; color:#E63946; font-size:1rem; margin-bottom:8px;">多渠道组合</div>
  413. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.6; margin-bottom:12px;">抖音+小红书+美团+私域,老带新提升LTV</div>
  414. <div style="display:flex; gap:12px;">
  415. <div style="text-align:center;"><div style="font-size:1.2rem; font-weight:700; color:#E63946;">500元</div><div style="color:var(--text-3); font-size:0.7rem;">客资ROI</div></div>
  416. <div style="text-align:center;"><div style="font-size:1.2rem; font-weight:700; color:#E63946;">3-5个</div><div style="color:var(--text-3); font-size:0.7rem;">渠道组合</div></div>
  417. </div>
  418. <div style="margin-top:12px; padding:8px 10px; background:#E6394615; border-radius:4px; font-size:0.78rem; color:#E63946;">H7假设覆盖: ${meta.hypotheses?.H7 || 0}条</div>
  419. </div>
  420. </div>
  421. </section>
  422. <!-- Ch7: 4P配称蓝图 -->
  423. <section class="report-section" id="ch7">
  424. <div class="section-head">
  425. <div class="section-eyebrow" style="color:#1D3557;">CHAPTER 07</div>
  426. <h2 class="text-h1">4P 配称蓝图</h2>
  427. <p class="section-sub">落地执行计划</p>
  428. </div>
  429. <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:14px; margin-bottom:24px;" class="grid-3">
  430. <div style="padding:18px 22px; background:#1D355708; border:1px solid #1D355720; border-radius:var(--radius-md);">
  431. <div style="font-weight:700; color:#1D3557; margin-bottom:10px;">阶段一(1-3月)</div>
  432. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.8;">
  433. <div>• 搭建抖音/小红书账号矩阵</div>
  434. <div>• 与南昌3-5家核心医院产科建立合作</div>
  435. <div>• 美团/大众点评店铺优化</div>
  436. <div>• 建立星级月嫂体系</div>
  437. </div>
  438. </div>
  439. <div style="padding:18px 22px; background:#F4A26108; border:1px solid #F4A26120; border-radius:var(--radius-md);">
  440. <div style="font-weight:700; color:#F4A261; margin-bottom:10px;">阶段二(3-6月)</div>
  441. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.8;">
  442. <div>• 短视频日常运营(3-5条/周)</div>
  443. <div>• 首部合作短剧(3-5万)</div>
  444. <div>• 微信私域搭建+老带新</div>
  445. <div>• 小红书投流测试</div>
  446. </div>
  447. </div>
  448. <div style="padding:18px 22px; background:#2D9C5D08; border:1px solid #2D9C5D20; border-radius:var(--radius-md);">
  449. <div style="font-weight:700; color:#2D9C5D; margin-bottom:10px;">阶段三(6-12月)</div>
  450. <div style="color:var(--text-2); font-size:0.85rem; line-height:1.8;">
  451. <div>• 多渠道付费投放规模化</div>
  452. <div>• 短剧系列化(3-5部)</div>
  453. <div>• 覆盖南昌80%+核心医院</div>
  454. <div>• 品牌定制短剧(20-50万)</div>
  455. </div>
  456. </div>
  457. </div>
  458. </section>
  459. <!-- Ch8: VOC高赞精选 -->
  460. <section class="report-section" id="ch8">
  461. <div class="section-head">
  462. <div class="section-eyebrow" style="color:#E63946;">CHAPTER 08</div>
  463. <h2 class="text-h1">VOC 高赞精选</h2>
  464. <p class="section-sub">真实用户声音</p>
  465. </div>
  466. <div style="margin-bottom:24px;">
  467. <div class="text-mono text-xs" style="color:#E63946; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">▲ 正面评价(高赞TOP6)</div>
  468. ${renderVocGrid(positives.slice(0, 6), 2)}
  469. </div>
  470. <div style="margin-bottom:24px;">
  471. <div class="text-mono text-xs" style="color:#8896a6; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">● 中性评价(高赞TOP6)</div>
  472. ${renderVocGrid(neutrals.slice(0, 6), 2)}
  473. </div>
  474. </section>
  475. <!-- 页脚 -->
  476. <div class="footer">
  477. <div>洪城到家 · VOC 深度洞察报告 v2</div>
  478. <div style="margin-top:8px;">数据来源:抖音评论采集 · ${meta.comments.toLocaleString()} 条真实VOC · Generated by OpenClaw</div>
  479. <div style="margin-top:4px;">${new Date().toLocaleDateString('zh-CN')}</div>
  480. </div>
  481. </div>
  482. <script>
  483. document.querySelectorAll('.report-section').forEach((section, i) => {
  484. section.style.opacity = '0';
  485. section.style.transform = 'translateY(16px)';
  486. setTimeout(() => {
  487. section.style.transition = 'opacity 0.4s ease, transform 0.4s ease';
  488. section.style.opacity = '1';
  489. section.style.transform = 'translateY(0)';
  490. }, i * 60);
  491. });
  492. </script>
  493. </body>
  494. </html>`;
  495. fs.writeFileSync(OUT_FILE, html, 'utf8');
  496. console.log('✅ 增强版报告已生成: ' + OUT_FILE);
  497. console.log(' VOC数据: ' + meta.comments.toLocaleString() + ' 条');
  498. console.log(' 假设覆盖: H1=' + (meta.hypotheses?.H1||0) + ' H4=' + (meta.hypotheses?.H4||0) + ' H5=' + (meta.hypotheses?.H5||0));