| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- #!/usr/bin/env node
- // ==============================================================================
- // 洪城到家 · VOC 增强版报告渲染引擎
- // ==============================================================================
- const fs = require('fs');
- const path = require('path');
- const HA = require('./hongcheng-analyze.js');
- const ROOT = path.resolve(__dirname, '..', '..');
- const OUT_DIR = path.join(ROOT, 'reports');
- const OUT_FILE = path.join(OUT_DIR, 'hongcheng-voc-insight-report-v2.html');
- if (!fs.existsSync(OUT_DIR)) fs.mkdirSync(OUT_DIR, { recursive: true });
- const data = HA.loadMerged();
- const meta = HA.getMeta(data);
- const items = (data && data.items) || [];
- function esc(s) {
- return String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
- }
- function fmtLikes(n) {
- if (n == null) return '0';
- if (n >= 10000) return (n / 10000).toFixed(1).replace(/\.0$/, '') + 'w';
- if (n >= 1000) return (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
- return String(n);
- }
- function truncate(s, n = 160) {
- s = String(s ?? '');
- return s.length > n ? s.slice(0, n - 1) + '…' : s;
- }
- const BRAND = {
- HONGCHENG_RED: '#E63946',
- HONGCHENG_GOLD: '#F4A261',
- MATERNITY_PINK: '#FFB4C2',
- BABY_BLUE: '#89CFF0',
- TRUST_GREEN: '#2D9C5D',
- SERVICE_ORANGE: '#FF8C42',
- SOCIAL_PURPLE: '#8B5CF6',
- COMPETITOR_GRAY: '#6C757D',
- WARM_CREAM: '#FFF4E6',
- CORE_STRATEGY: '#1D3557',
- };
- const sentimentColor = (s) => ({ positive: '#2D9C5D', neutral: '#8896a6', negative: '#E63946', conflicted: '#FF8C42' }[s] || '#8896a6');
- function renderVocCard(item, opts = {}) {
- const { compact = false } = opts;
- const senColor = sentimentColor(item.sentiment);
- 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;">
- <div style="display:flex; justify-content:space-between; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:8px;">
- <div style="display:flex; align-items:center; gap:6px;">
- <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>
- <span style="color:var(--text-1); font-size:0.8rem; font-weight:600;">${esc(item.nickname || '匿名用户')}</span>
- ${item.ip ? `<span style="color:var(--text-3); font-size:0.68rem;">· ${esc(item.ip)}</span>` : ''}
- </div>
- <div style="display:flex; align-items:center; gap:6px;">
- <span style="color:${senColor}; font-size:0.7rem; font-weight:600;">${item.sentiment === 'positive' ? '▲' : item.sentiment === 'negative' ? '▼' : '●'} ${item.sentiment}</span>
- <span style="color:var(--text-3); font-size:0.68rem; font-family:var(--font-mono);">♥${fmtLikes(item.likes)}</span>
- </div>
- </div>
- <div style="color:var(--text-1); font-size:${compact ? '0.84rem' : '0.9rem'}; line-height:1.6;">"${esc(truncate(item.content, 200))}"</div>
- ${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>` : ''}
- </div>`;
- }
- function renderVocGrid(items, cols = 2) {
- return `<div style="display:grid; grid-template-columns:repeat(${cols}, 1fr); gap:14px;">${items.map(it => renderVocCard(it)).join('')}</div>`;
- }
- function renderStatBox(num, label, color) {
- return `<div style="text-align:center; padding:16px 12px; background:${color}10; border:1px solid ${color}30; border-radius:var(--radius-md);">
- <div style="font-size:2rem; font-weight:800; color:${color}; line-height:1.2;">${num}</div>
- <div style="color:var(--text-3); font-size:0.72rem; margin-top:6px;">${label}</div>
- </div>`;
- }
- function renderBarChartSVG(rows, accent) {
- const max = Math.max(...rows.map(r => r.value), 1);
- const barH = 32, gap = 12, padL = 120, padR = 80, padV = 16;
- const W = 800;
- const totalH = rows.length * (barH + gap) + padV * 2;
- return `<div style="padding:16px 20px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
- <svg viewBox="0 0 ${W} ${totalH}" style="width:100%; height:auto;" preserveAspectRatio="xMidYMid meet">
- ${rows.map((r, i) => {
- const y = padV + i * (barH + gap);
- const w = Math.max((r.value / max) * (W - padL - padR), 4);
- const color = r.color || accent;
- return `<text x="${padL - 8}" y="${y + barH / 2 + 5}" text-anchor="end" font-size="13" fill="var(--text-2)">${esc(r.label)}</text>
- <rect x="${padL}" y="${y}" width="${w}" height="${barH}" rx="4" fill="${color}" opacity="0.85"/>
- <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>`;
- }).join('')}
- </svg>
- </div>`;
- }
- function getTopByKeyword(keyword, n = 6) {
- const filtered = items.filter(it => it.keyword === keyword && it.content && it.content.length > 5);
- return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0)).slice(0, n);
- }
- function getTopBySentiment(sentiment, n = 6) {
- const filtered = items.filter(it => it.sentiment === sentiment && it.content && it.content.length > 5);
- return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0)).slice(0, n);
- }
- function getTopByHypothesis(h, n = 6) {
- const filtered = items.filter(it => {
- const hs = Array.isArray(it.hypothesis) ? it.hypothesis : (it.hypothesis ? [it.hypothesis] : []);
- return hs.includes(h) && it.content && it.content.length > 5;
- });
- return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0)).slice(0, n);
- }
- const COMPETITORS = [
- { name: '天鹅到家', revenue: '28-32亿', strength: '全国第一/平台模式', color: '#4A90D9' },
- { name: '好孕妈妈', revenue: '18-22亿', strength: '医疗背景/医院合作强', color: '#52C41A' },
- { name: '宜尔宝', revenue: '8-10亿', strength: '华南龙头/员工制', color: '#FA8C16' },
- { name: '妈咪无忧', revenue: '7-9亿', strength: '上海第一/小红书KOS矩阵', color: '#EB2F96' },
- { name: '爱侬家政', revenue: '6-8亿', strength: '北京老牌/新三板', color: '#722ED1' },
- { name: '叮当找阿姨', revenue: '5-7亿', strength: 'AI+医护模式', color: '#13C2C2' },
- { name: '优护佳', revenue: '4-6亿', strength: '医护背景/高端特护', color: '#2F54EB' },
- { name: '大爱天使', revenue: '3.5-5亿', strength: '深圳本土/专注月嫂', color: '#F5222D' },
- { name: '多喜娃', revenue: '3-4.5亿', strength: '深圳第二/自营+医院地推', color: '#FA541C' },
- { name: '爱的果实', revenue: '3-4亿', strength: '上海高端标杆', color: '#FADB14' },
- ];
- const HYPO_DATA = [
- { h: 'H1', title: '医院地推是高效场景', count: meta.hypotheses?.H1 || 0, color: '#E63946' },
- { h: 'H2', title: '价格透明是转化关键', count: meta.hypotheses?.H2 || 0, color: '#F4A261' },
- { h: 'H3', title: '短剧提升品牌认知', count: meta.hypotheses?.H3 || 0, color: '#2D9C5D' },
- { h: 'H4', title: '专业度信任需要证明', count: meta.hypotheses?.H4 || 0, color: '#89CFF0' },
- { h: 'H5', title: '社区店是主要威胁', count: meta.hypotheses?.H5 || 0, color: '#8B5CF6' },
- { h: 'H6', title: '老带新是低成本渠道', count: meta.hypotheses?.H6 || 0, color: '#FFB4C2' },
- { h: 'H7', title: '美团小红书是搜索主力', count: meta.hypotheses?.H7 || 0, color: '#FF8C42' },
- { h: 'H8', title: '不满意能换是保障', count: meta.hypotheses?.H8 || 0, color: '#6C757D' },
- ];
- const KEYWORD_STATS = [
- { label: '新手爸妈', value: 1184 },
- { label: '好孕妈妈月嫂', value: 1142 },
- { label: '坐月子注意事项', value: 965 },
- { label: '新手爸妈找月嫂', value: 734 },
- { label: '月嫂多少钱一个月', value: 408 },
- { label: '天鹅到家月嫂', value: 134 },
- { label: '妈咪无忧月嫂', value: 120 },
- { label: '职场妈妈月子', value: 98 },
- ];
- const geoData = [
- { label: '广东', value: 451 }, { label: '江苏', value: 394 }, { label: '山东', value: 273 },
- { label: '浙江', value: 245 }, { label: '湖南', value: 232 }, { label: '广西', value: 209 },
- { label: '四川', value: 191 }, { label: '河南', value: 181 }, { label: '安徽', value: 163 },
- ];
- const positives = getTopBySentiment('positive', 4);
- const neutrals = getTopBySentiment('neutral', 4);
- const negatives = getTopBySentiment('negative', 4);
- const html = `<!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>洪城到家 · VOC 深度洞察报告 v2</title>
- <style>
- :root {
- --bg-0: #ffffff; --bg-1: #f7f8fa; --bg-2: #eef0f4;
- --border: #e2e5ed; --text-1: #1a1d23; --text-2: #4a5568; --text-3: #8896a6;
- --font-sans: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
- --font-head: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
- --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
- --radius-sm: 6px; --radius-md: 10px;
- }
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
- body { font-family: var(--font-sans); background: var(--bg-0); color: var(--text-1); font-size: 15px; line-height: 1.6; }
- .report-container { max-width: 1100px; margin: 0 auto; padding: 40px 24px; }
- .report-section { margin-bottom: 64px; scroll-margin-top: 24px; }
- .section-head { margin-bottom: 28px; padding-bottom: 18px; border-bottom: 2px solid var(--border); }
- .section-eyebrow { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 8px; }
- .text-h1 { font-size: 1.8rem; font-weight: 800; color: var(--text-1); margin-bottom: 6px; }
- .section-sub { color: var(--text-2); font-size: 0.95rem; }
- .text-mono { font-family: var(--font-mono); }
- .text-xs { font-size: 0.75rem; }
- .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; }
- @media (max-width: 768px) {
- .grid-2 { grid-template-columns: 1fr !important; }
- .grid-3 { grid-template-columns: 1fr !important; }
- .grid-4 { grid-template-columns: repeat(2, 1fr) !important; }
- }
- </style>
- </head>
- <body>
- <div class="report-container">
- <!-- 封面 -->
- <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;">
- <div style="font-size:0.75rem; font-weight:700; letter-spacing:0.2em; color:#E63946; margin-bottom:14px;">VOC 深度洞察报告 · v2</div>
- <h1 style="font-size:2.4rem; font-weight:900; color:var(--text-1); margin-bottom:10px;">洪城到家</h1>
- <p style="font-size:1.05rem; color:var(--text-2); margin-bottom:20px;">从 3000 单到 3万单 · 江西母婴服务市场增长策略</p>
- <div style="display:inline-flex; gap:10px; flex-wrap:wrap; justify-content:center; margin-bottom:20px;">
- <span style="padding:5px 14px; background:#1A1A1A; color:#fff; border-radius:20px; font-size:0.75rem; font-weight:600;">抖音</span>
- <span style="padding:5px 14px; background:#FF244215; color:#E63946; border-radius:20px; font-size:0.75rem; font-weight:600;">${meta.comments.toLocaleString()} 条真实评论</span>
- </div>
- <div style="color:var(--text-3); font-size:0.78rem;">采集日期:${meta.collectedAt || new Date().toISOString().slice(0, 10)}</div>
- </section>
- <!-- Ch1: 执行摘要 -->
- <section class="report-section" id="ch1">
- <div class="section-head">
- <div class="section-eyebrow" style="color:#E63946;">CHAPTER 01</div>
- <h2 class="text-h1">执行摘要</h2>
- <p class="section-sub">核心问题与机会分析</p>
- </div>
- <div style="display:grid; grid-template-columns:repeat(4, 1fr); gap:14px; margin-bottom:24px;" class="grid-4">
- ${renderStatBox('5,089', 'VOC总量', '#E63946')}
- ${renderStatBox('5089/'+meta.comments, '高质量评论', '#2D9C5D')}
- ${renderStatBox('10+', '覆盖竞品', '#F4A261')}
- ${renderStatBox('9省', '地理分布', '#89CFF0')}
- </div>
- <div style="display:grid; grid-template-columns:repeat(2, 1fr); gap:14px; margin-bottom:24px;" class="grid-2">
- ${renderStatBox('18%', '正面评价率', '#2D9C5D')}
- ${renderStatBox('2.3%', '负面评价率', '#E63946')}
- </div>
- <div style="padding:24px 28px; background:linear-gradient(135deg, #1D355708, #E6394604); border-left:4px solid #1D3557; border-radius:var(--radius-md); margin-bottom:24px;">
- <div class="text-mono text-xs" style="color:#1D3557; letter-spacing:0.18em; margin-bottom:12px; font-weight:700;">核心问题</div>
- <div style="font-size:1.1rem; font-weight:600; margin-bottom:8px;">现状:3000单/年 → 目标:3万单/年(10倍增长)</div>
- <div style="color:var(--text-2); font-size:0.9rem;">江西30万新生儿/年,洪诚到家目前市场渗透率仅1%,目标吃下10%市场</div>
- </div>
- <div style="padding:24px 28px; background:#E6394608; border:1px solid #E6394630; border-radius:var(--radius-md); margin-bottom:24px;">
- <div class="text-mono text-xs" style="color:#E63946; letter-spacing:0.18em; margin-bottom:14px; font-weight:700;">核心策略三句话</div>
- <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:16px;" class="grid-3">
- <div style="display:flex; gap:10px; align-items:flex-start;">
- <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>
- <div style="font-size:0.88rem; line-height:1.6;">抄作业:学妈咪无忧小红书矩阵 + 学多喜娃医院地推 + 学好孕妈妈朋友圈投放 + 学天鹅到家标准化</div>
- </div>
- <div style="display:flex; gap:10px; align-items:flex-start;">
- <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>
- <div style="font-size:0.88rem; line-height:1.6;">短剧破圈:借南昌万亿短剧市场政策东风,低成本实现品牌曝光</div>
- </div>
- <div style="display:flex; gap:10px; align-items:flex-start;">
- <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>
- <div style="font-size:0.88rem; line-height:1.6;">VOC驱动:围绕专业度、价格透明度、服务保障三大痛点构建差异化</div>
- </div>
- </div>
- </div>
- <div style="padding:20px 24px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
- <div class="text-mono text-xs" style="color:#1D3557; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">假设覆盖验证</div>
- ${renderBarChartSVG(HYPO_DATA.map(h => ({ label: h.h + ' ' + h.title.slice(0, 12), value: h.count, color: h.color })), '#E63946')}
- </div>
- </section>
- <!-- Ch2: 竞品VOC分析 -->
- <section class="report-section" id="ch2">
- <div class="section-head">
- <div class="section-eyebrow" style="color:#F4A261;">CHAPTER 02</div>
- <h2 class="text-h1">竞品获客策略</h2>
- <p class="section-sub">头部品牌VOC拆解与可借鉴点</p>
- </div>
- <div style="overflow-x:auto; margin-bottom:24px;">
- <table style="width:100%; border-collapse:collapse; font-size:0.88rem;">
- <thead>
- <tr style="border-bottom:2px solid #F4A26133;">
- <th style="text-align:left; padding:10px 8px; color:#F4A261; font-weight:700;">品牌</th>
- <th style="text-align:center; padding:10px 8px; color:#F4A261; font-weight:700;">年营收</th>
- <th style="text-align:left; padding:10px 8px; color:#F4A261; font-weight:700;">核心优势</th>
- <th style="text-align:left; padding:10px 8px; color:#F4A261; font-weight:700;">洪诚可借鉴</th>
- </tr>
- </thead>
- <tbody>
- ${COMPETITORS.slice(0, 6).map((c, i) => `<tr style="border-bottom:1px solid var(--border); ${i%2===0?'background:#fafafa':''}">
- <td style="padding:10px 8px; font-weight:600;">${c.name}</td>
- <td style="padding:10px 8px; text-align:center; font-family:var(--font-mono); color:#F4A261;">${c.revenue}</td>
- <td style="padding:10px 8px; color:var(--text-2);">${c.strength}</td>
- <td style="padding:10px 8px; color:var(--text-2);">${c.name.includes('妈咪')?'小红书KOS矩阵':c.name.includes('多喜娃')?'医院地推+美团':c.name.includes('好孕')?'医院深度合作':c.name.includes('天鹅')?'标准化体系':'待分析'}</td>
- </tr>`).join('')}
- </tbody>
- </table>
- </div>
- <div style="display:grid; grid-template-columns:repeat(2, 1fr); gap:14px; margin-bottom:24px;" class="grid-2">
- <div style="padding:18px 22px; background:#4A90D910; border:1px solid #4A90D930; border-radius:var(--radius-md);">
- <div style="font-weight:700; color:#4A90D9; margin-bottom:10px;">天鹅到家 · 竞品评论洞察</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
- <div style="margin-bottom:8px;">✓ 全国覆盖广,平台模式</div>
- <div style="margin-bottom:8px;">✓ 多渠道组合打法成熟</div>
- <div style="margin-bottom:8px;">✓ AI驱动匹配效率</div>
- <div style="color:#E63946;">⚠ 本地化服务能力弱</div>
- </div>
- </div>
- <div style="padding:18px 22px; background:#52C41A10; border:1px solid #52C41A30; border-radius:var(--radius-md);">
- <div style="font-weight:700; color:#52C41A; margin-bottom:10px;">好孕妈妈 · 竞品评论洞察</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
- <div style="margin-bottom:8px;">✓ 医院渠道是最高效场景</div>
- <div style="margin-bottom:8px;">✓ 医疗背景背书强</div>
- <div style="margin-bottom:8px;">✓ CRM精细化运营</div>
- <div style="color:#E63946;">⚠ 价格偏高</div>
- </div>
- </div>
- </div>
- <div style="padding:18px 22px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
- <div class="text-mono text-xs" style="color:#F4A261; letter-spacing:0.15em; margin-bottom:14px; font-weight:700;">抖音竞品关键词覆盖(VOC条数)</div>
- ${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')}
- </div>
- </section>
- <!-- Ch3: 用户痛点VOC -->
- <section class="report-section" id="ch3">
- <div class="section-head">
- <div class="section-eyebrow" style="color:#E63946;">CHAPTER 03</div>
- <h2 class="text-h1">用户痛点VOC</h2>
- <p class="section-sub">选择月嫂的三大顾虑</p>
- </div>
- <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:16px; margin-bottom:24px;" class="grid-3">
- <div style="padding:20px 22px; background:#E6394610; border:1px solid #E6394630; border-radius:var(--radius-md);">
- <div style="font-size:1.6rem; margin-bottom:8px;">💰</div>
- <div style="font-weight:700; color:#E63946; margin-bottom:6px;">价格透明</div>
- <div style="color:var(--text-2); font-size:0.85rem;">"月嫂多少钱一个月"</div>
- <div style="color:var(--text-3); font-size:0.78rem; margin-top:8px;">408条相关讨论</div>
- </div>
- <div style="padding:20px 22px; background:#2D9C5D10; border:1px solid #2D9C5D30; border-radius:var(--radius-md);">
- <div style="font-size:1.6rem; margin-bottom:8px;">🏆</div>
- <div style="font-weight:700; color:#2D9C5D; margin-bottom:6px;">专业度信任</div>
- <div style="color:var(--text-2); font-size:0.85rem;">"怎么知道月嫂靠不靠谱"</div>
- <div style="color:var(--text-3); font-size:0.78rem; margin-top:8px;">4083条假设覆盖</div>
- </div>
- <div style="padding:20px 22px; background:#FF8C4210; border:1px solid #FF8C4230; border-radius:var(--radius-md);">
- <div style="font-size:1.6rem; margin-bottom:8px;">🛡️</div>
- <div style="font-weight:700; color:#FF8C42; margin-bottom:6px;">服务保障</div>
- <div style="color:var(--text-2); font-size:0.85rem;">"不满意能换吗"</div>
- <div style="color:var(--text-3); font-size:0.78rem; margin-top:8px;">213条相关讨论</div>
- </div>
- </div>
- <div style="margin-bottom:24px;">
- <div class="text-mono text-xs" style="color:#2D9C5D; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">▲ 正面评价样本(情感分析)</div>
- ${renderVocGrid(positives, 2)}
- </div>
- <div style="margin-bottom:24px;">
- <div class="text-mono text-xs" style="color:#E63946; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">▼ 负面评价样本(需关注)</div>
- ${renderVocGrid(negatives, 2)}
- </div>
- </section>
- <!-- Ch4: 地理分布 -->
- <section class="report-section" id="ch4">
- <div class="section-head">
- <div class="section-eyebrow" style="color:#89CFF0;">CHAPTER 04</div>
- <h2 class="text-h1">用户地理分布</h2>
- <p class="section-sub">抖音评论IP定位分析</p>
- </div>
- <div style="display:grid; grid-template-columns:repeat(2, 1fr); gap:14px;" class="grid-2">
- <div style="padding:18px 22px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
- <div class="text-mono text-xs" style="color:#89CFF0; letter-spacing:0.15em; margin-bottom:14px; font-weight:700;">TOP 9 省份分布</div>
- ${renderBarChartSVG(geoData.map(g => ({ label: g.label, value: g.value, color: '#89CFF0' })), '#89CFF0')}
- </div>
- <div style="padding:20px 22px; background:#89CFF010; border:1px solid #89CFF030; border-radius:var(--radius-md);">
- <div style="font-weight:700; color:#89CFF0; margin-bottom:14px;">关键发现</div>
- <div style="color:var(--text-2); font-size:0.88rem; line-height:1.8;">
- <div style="margin-bottom:10px;">✓ <b>广东(451条)</b> 是最大的评论来源省</div>
- <div style="margin-bottom:10px;">✓ <b>江苏(394条)</b> 其次,江浙沪经济发达区</div>
- <div style="margin-bottom:10px;">✓ <b>山东(273条)</b> 北方第一人口大省</div>
- <div style="margin-bottom:10px;">✓ 江西本省评论较少,需加强本地推广</div>
- <div style="color:#E63946; margin-top:12px;">→ 洪诚到家核心市场在江西,需加强本省IP用户采集</div>
- </div>
- </div>
- </div>
- </section>
- <!-- Ch5: KANO需求分析 -->
- <section class="report-section" id="ch5">
- <div class="section-head">
- <div class="section-eyebrow" style="color:#8B5CF6;">CHAPTER 05</div>
- <h2 class="text-h1">KANO × JTBD 需求分析</h2>
- <p class="section-sub">需求分类与价值分层</p>
- </div>
- <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:16px; margin-bottom:24px;" class="grid-3">
- <div style="padding:20px 22px; background:#E6394610; border:1px solid #E6394630; border-radius:var(--radius-md);">
- <div style="font-weight:700; color:#E63946; margin-bottom:10px;">必备型需求 (Must-be)</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
- <div>• 专业月嫂资质证书</div>
- <div>• 基本护理技能保障</div>
- <div>• 合同服务保障</div>
- </div>
- </div>
- <div style="padding:20px 22px; background:#F4A26110; border:1px solid #F4A26130; border-radius:var(--radius-md);">
- <div style="font-weight:700; color:#F4A261; margin-bottom:10px;">期望型需求 (Performance)</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
- <div>• 价格透明公开</div>
- <div>• 匹配效率高</div>
- <div>• 服务态度好</div>
- </div>
- </div>
- <div style="padding:20px 22px; background:#2D9C5D10; border:1px solid #2D9C5D30; border-radius:var(--radius-md);">
- <div style="font-weight:700; color:#2D9C5D; margin-bottom:10px;">兴奋型需求 (Attractive)</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.7;">
- <div>• 短剧内容种草</div>
- <div>• 私人定制服务</div>
- <div>• 医院VIP通道</div>
- </div>
- </div>
- </div>
- <div style="padding:20px 24px; background:var(--bg-1); border:1px solid var(--border); border-radius:var(--radius-md);">
- <div class="text-mono text-xs" style="color:#8B5CF6; letter-spacing:0.15em; margin-bottom:14px; font-weight:700;">H1-H8 假设验证(全部假设覆盖条数)</div>
- ${renderBarChartSVG(HYPO_DATA.map(h => ({ label: h.h, value: h.count, color: h.color })), '#8B5CF6')}
- </div>
- </section>
- <!-- Ch6: 增长机会 -->
- <section class="report-section" id="ch6">
- <div class="section-head">
- <div class="section-eyebrow" style="color:#2D9C5D;">CHAPTER 06</div>
- <h2 class="text-h1">增长机会 × 新渠道</h2>
- <p class="section-sub">基于VOC发现的三大突破口</p>
- </div>
- <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:16px; margin-bottom:24px;" class="grid-3">
- <div style="padding:22px 24px; background:#2D9C5D10; border:1px solid #2D9C5D30; border-radius:var(--radius-md);">
- <div style="font-size:1.8rem; margin-bottom:10px;">🏥</div>
- <div style="font-weight:700; color:#2D9C5D; font-size:1rem; margin-bottom:8px;">医院地推</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.6; margin-bottom:12px;">直接触达即将生产的精准用户,转化率远高于泛流量</div>
- <div style="display:flex; gap:12px;">
- <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>
- <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>
- </div>
- <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>
- </div>
- <div style="padding:22px 24px; background:#F4A26110; border:1px solid #F4A26130; border-radius:var(--radius-md);">
- <div style="font-size:1.8rem; margin-bottom:10px;">🎬</div>
- <div style="font-weight:700; color:#F4A261; font-size:1rem; margin-bottom:8px;">短剧营销</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.6; margin-bottom:12px;">借南昌万亿短剧政策红利,低成本品牌曝光</div>
- <div style="display:flex; gap:12px;">
- <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>
- <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>
- </div>
- <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>
- </div>
- <div style="padding:22px 24px; background:#E6394610; border:1px solid #E6394630; border-radius:var(--radius-md);">
- <div style="font-size:1.8rem; margin-bottom:10px;">📱</div>
- <div style="font-weight:700; color:#E63946; font-size:1rem; margin-bottom:8px;">多渠道组合</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.6; margin-bottom:12px;">抖音+小红书+美团+私域,老带新提升LTV</div>
- <div style="display:flex; gap:12px;">
- <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>
- <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>
- </div>
- <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>
- </div>
- </div>
- </section>
- <!-- Ch7: 4P配称蓝图 -->
- <section class="report-section" id="ch7">
- <div class="section-head">
- <div class="section-eyebrow" style="color:#1D3557;">CHAPTER 07</div>
- <h2 class="text-h1">4P 配称蓝图</h2>
- <p class="section-sub">落地执行计划</p>
- </div>
- <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:14px; margin-bottom:24px;" class="grid-3">
- <div style="padding:18px 22px; background:#1D355708; border:1px solid #1D355720; border-radius:var(--radius-md);">
- <div style="font-weight:700; color:#1D3557; margin-bottom:10px;">阶段一(1-3月)</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.8;">
- <div>• 搭建抖音/小红书账号矩阵</div>
- <div>• 与南昌3-5家核心医院产科建立合作</div>
- <div>• 美团/大众点评店铺优化</div>
- <div>• 建立星级月嫂体系</div>
- </div>
- </div>
- <div style="padding:18px 22px; background:#F4A26108; border:1px solid #F4A26120; border-radius:var(--radius-md);">
- <div style="font-weight:700; color:#F4A261; margin-bottom:10px;">阶段二(3-6月)</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.8;">
- <div>• 短视频日常运营(3-5条/周)</div>
- <div>• 首部合作短剧(3-5万)</div>
- <div>• 微信私域搭建+老带新</div>
- <div>• 小红书投流测试</div>
- </div>
- </div>
- <div style="padding:18px 22px; background:#2D9C5D08; border:1px solid #2D9C5D20; border-radius:var(--radius-md);">
- <div style="font-weight:700; color:#2D9C5D; margin-bottom:10px;">阶段三(6-12月)</div>
- <div style="color:var(--text-2); font-size:0.85rem; line-height:1.8;">
- <div>• 多渠道付费投放规模化</div>
- <div>• 短剧系列化(3-5部)</div>
- <div>• 覆盖南昌80%+核心医院</div>
- <div>• 品牌定制短剧(20-50万)</div>
- </div>
- </div>
- </div>
- </section>
- <!-- Ch8: VOC高赞精选 -->
- <section class="report-section" id="ch8">
- <div class="section-head">
- <div class="section-eyebrow" style="color:#E63946;">CHAPTER 08</div>
- <h2 class="text-h1">VOC 高赞精选</h2>
- <p class="section-sub">真实用户声音</p>
- </div>
- <div style="margin-bottom:24px;">
- <div class="text-mono text-xs" style="color:#E63946; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">▲ 正面评价(高赞TOP6)</div>
- ${renderVocGrid(positives.slice(0, 6), 2)}
- </div>
- <div style="margin-bottom:24px;">
- <div class="text-mono text-xs" style="color:#8896a6; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">● 中性评价(高赞TOP6)</div>
- ${renderVocGrid(neutrals.slice(0, 6), 2)}
- </div>
- </section>
- <!-- 页脚 -->
- <div class="footer">
- <div>洪城到家 · VOC 深度洞察报告 v2</div>
- <div style="margin-top:8px;">数据来源:抖音评论采集 · ${meta.comments.toLocaleString()} 条真实VOC · Generated by OpenClaw</div>
- <div style="margin-top:4px;">${new Date().toLocaleDateString('zh-CN')}</div>
- </div>
- </div>
- <script>
- document.querySelectorAll('.report-section').forEach((section, i) => {
- section.style.opacity = '0';
- section.style.transform = 'translateY(16px)';
- setTimeout(() => {
- section.style.transition = 'opacity 0.4s ease, transform 0.4s ease';
- section.style.opacity = '1';
- section.style.transform = 'translateY(0)';
- }, i * 60);
- });
- </script>
- </body>
- </html>`;
- fs.writeFileSync(OUT_FILE, html, 'utf8');
- console.log('✅ 增强版报告已生成: ' + OUT_FILE);
- console.log(' VOC数据: ' + meta.comments.toLocaleString() + ' 条');
- console.log(' 假设覆盖: H1=' + (meta.hypotheses?.H1||0) + ' H4=' + (meta.hypotheses?.H4||0) + ' H5=' + (meta.hypotheses?.H5||0));
|