| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- // ==============================================================================
- // 乳酸菌 v2 · Ch3 儿童微生态 · 交叉验证(Step 4)
- // ==============================================================================
- // 3.1 儿童微生态制剂全量扫描 · 妈咪爱/亿活/合生元等
- // 3.2 交叉验证 · 竞品没满足 + 我们能满足的空白
- // 3.3 竞品正反面精简分析 · 药品 vs 食品 vs 营养品
- // ==============================================================================
- const LA = require('./lactic-analyze.js');
- const C = require('./lactic-components.js');
- const stripTags = s => String(s || '').replace(/#[^\s#]+/g, '').trim();
- const { BRAND, esc, renderSectionHead, renderVocCard, renderVocGrid,
- renderStatStrip, renderBarChart, renderCompareTable, renderInsightHero,
- renderFindingCard, renderDecisionList, renderMatrix2x2, renderTagCloud } = C;
- // ============================================================
- // 3.1 儿童微生态制剂全量扫描
- // ============================================================
- function renderSub1({ chapter: c, subIdx, sub, slideNum, slideTotal, data, meta }) {
- const items = data?.items || [];
- // 儿童竞品分组
- const childProducts = [
- { name: '妈咪爱', kw: /妈咪爱/i, type: '药品·散剂', color: BRAND.CORAL },
- { name: '合生元益生菌', kw: /合生元/i, type: '食品·粉剂', color: BRAND.JIANG_GREEN },
- { name: '万益蓝益生菌', kw: /万益蓝/i, type: '食品·滴剂/粉', color: BRAND.SKY_BLUE },
- { name: '亿活(布拉氏酵母菌)', kw: /亿活/i, type: '药品·胶囊/散剂', color: BRAND.PURPLE },
- { name: '拜奥益生菌', kw: /拜奥/i, type: '食品·滴剂', color: BRAND.WARM_ORANGE },
- { name: 'lifespace 儿童', kw: /lifespace/i, type: '食品·粉剂', color: BRAND.LACTIC_MINT },
- { name: '宝乐安(酪酸梭菌)', kw: /宝乐安/i, type: '药品·散剂', color: BRAND.SAGE },
- { name: 'inne 噗噗宝', kw: /inne|噗噗/i, type: '食品·软糖', color: BRAND.BERRY_PINK },
- { name: '小胖瓶益生菌', kw: /小胖瓶/i, type: '食品·滴剂', color: BRAND.GOLD },
- { name: '康萃乐益生菌', kw: /康萃乐/i, type: '食品·咀嚼片', color: BRAND.TECH_BLUE },
- ];
- const productStats = childProducts.map((cp, ci) => {
- const matched = items.filter(it => cp.kw.test(`${it.keyword || ''} ${it.product || ''} ${it.content || ''}`));
- const substMatched = matched.filter(it => stripTags(it.content).length >= 25);
- const topVoc = LA.getEvidence(substMatched, { top: 1, seed: 301 + ci, minChars: 20, contentMatch: /益生菌|乳酸菌|肠|便秘|消化|菌|拉肚|好吃|口味|效果|推荐|宝宝|孩子/i });
- return { ...cp, count: matched.length, topVoc };
- }).sort((a, b) => b.count - a.count);
- return `<section class="report-section" id="${c.key}-${subIdx}" data-tone="${c.tone}" data-slide-num="${slideNum}/${slideTotal}">
- <div class="section-inner">
- ${renderSectionHead({
- eyebrow: `${c.num}.${subIdx} · CHILDREN PRODUCT SCAN`,
- title: sub,
- subtitle: '王总要求:"用微生态制剂大品类中的儿童部分来做交叉验证"',
- color: c.color,
- })}
- ${renderBarChart(
- productStats.map(ps => ({
- label: `${ps.name}(${ps.type})`,
- value: ps.count,
- valueLabel: `${ps.count} 条`,
- color: ps.color,
- })),
- { accent: c.color, title: '儿童微生态产品 VOC 覆盖量', height: 340 }
- )}
- <!-- 每个竞品的代表性 VOC 原声 -->
- <div style="margin-top:20px;">
- <div class="text-mono text-xs" style="color:${c.color}; letter-spacing:0.15em; margin-bottom:12px; font-weight:700;">各竞品代表性 VOC 原声</div>
- <div style="display:grid; grid-template-columns:repeat(2, 1fr); gap:10px;">
- ${productStats.filter(ps => ps.topVoc.length > 0).slice(0, 6).map(ps => `
- <div style="padding:10px 12px; background:${ps.color}06; border-left:3px solid ${ps.color}; border-radius:4px;">
- <div style="font-size:0.7rem; font-weight:700; color:${ps.color}; margin-bottom:4px;">${esc(ps.name)}(${ps.count} 条)</div>
- <div style="font-size:0.74rem; color:var(--text-1); line-height:1.45;">"${esc((ps.topVoc[0].content || '').slice(0, 80))}"</div>
- <div style="color:var(--text-3); font-size:0.6rem; margin-top:3px;">—${esc(ps.topVoc[0].nickname || ps.topVoc[0].platform)} · ❤️${ps.topVoc[0].likes || 0}</div>
- </div>`).join('')}
- </div>
- </div>
- <div style="display:grid; grid-template-columns:repeat(3, 1fr); gap:14px; margin-top:24px;">
- <div style="padding:18px; background:${BRAND.CORAL}08; border:1px solid ${BRAND.CORAL}33; border-radius:var(--radius-sm);">
- <div class="text-mono text-xs" style="color:${BRAND.CORAL}; font-weight:700; margin-bottom:8px;">💊 药品级</div>
- <div style="color:var(--text-1); font-size:0.84rem; line-height:1.6;">
- 妈咪爱(枯草+肠球菌)<br/>亿活(布拉氏酵母菌)<br/>宝乐安(酪酸梭菌)<br/>
- <strong>特点</strong>:医生处方/推荐,急性腹泻首选<br/>
- <strong>弱点</strong>:散剂喂药难,部分需冷链
- </div>
- </div>
- <div style="padding:18px; background:${BRAND.JIANG_GREEN}08; border:1px solid ${BRAND.JIANG_GREEN}33; border-radius:var(--radius-sm);">
- <div class="text-mono text-xs" style="color:${BRAND.JIANG_GREEN}; font-weight:700; margin-bottom:8px;">🥛 食品级</div>
- <div style="color:var(--text-1); font-size:0.84rem; line-height:1.6;">
- 合生元 / 万益蓝 / 拜奥<br/>lifespace / inne / 康萃乐<br/>
- <strong>特点</strong>:家长自主选购,种草驱动<br/>
- <strong>弱点</strong>:无功效背书,"智商税"争议
- </div>
- </div>
- <div style="padding:18px; background:${BRAND.LACTIC_MINT}08; border:1px solid ${BRAND.LACTIC_MINT}33; border-radius:var(--radius-sm);">
- <div class="text-mono text-xs" style="color:${BRAND.LACTIC_MINT}; font-weight:700; margin-bottom:8px;">⭐ 乳酸菌素片(我们)</div>
- <div style="color:var(--text-1); font-size:0.84rem; line-height:1.6;">
- 江中乳酸菌素片儿童版<br/>
- <strong>身份</strong>:OTC 药品(蓝帽子)<br/>
- <strong>剂型</strong>:咀嚼片(奶片口感)<br/>
- <strong>独特</strong>:药品信任 + 食品体验<br/>
- <strong>空位</strong>:药品中唯一咀嚼片
- </div>
- </div>
- </div>
- </div>
- </section>`;
- }
- // ============================================================
- // 3.2 交叉验证 · 竞品空白 + 我们能接住
- // ============================================================
- function renderSub2({ chapter: c, subIdx, sub, slideNum, slideTotal, data, meta }) {
- const items = data?.items || [];
- // 竞品差评中的机会
- const negItems = items.filter(it => it.sentiment === 'negative');
- const painCategories = [
- { pain: '喂药难/孩子抗拒', re: /喂药|难喂|吐出|抗拒|不肯吃|不爱吃|哭/i, ourAnswer: '咀嚼片像奶片,孩子主动要', score: 95 },
- { pain: '效果不明显/智商税', re: /没用|没效|智商税|没感觉|浪费钱/i, ourAnswer: 'OTC 药品功效有保障', score: 85 },
- { pain: '需要冷链/保存不便', re: /冷链|冰箱|保存|过期|变质/i, ourAnswer: '常温保存,便携', score: 80 },
- { pain: '价格贵/不值', re: /贵|价格|不值|性价比/i, ourAnswer: 'OTC 药店渠道定价合理', score: 70 },
- { pain: '成分复杂/不放心', re: /成分|添加|不放心|化学/i, ourAnswer: '灭活菌素,成分简单', score: 75 },
- { pain: '不知道该选哪个', re: /纠结|选择|哪个好|推荐/i, ourAnswer: '"药品级肠道调理+奶片口感"清晰定位', score: 90 },
- ];
- const painStats = painCategories.map(pc => ({
- ...pc,
- count: negItems.filter(it => pc.re.test(it.content || '')).length,
- })).sort((a, b) => b.count - a.count);
- // 找差评原声
- const painEvidence = LA.getEvidence(negItems, {
- top: 4, seed: 31, minChars: 30,
- contentMatch: /喂药|难喂|没用|智商税|冷链|贵|副作用|拉肚|不吃|吐|抗拒/i,
- });
- return `<section class="report-section" id="${c.key}-${subIdx}" data-tone="${c.tone}" data-slide-num="${slideNum}/${slideTotal}">
- <div class="section-inner">
- ${renderSectionHead({
- eyebrow: `${c.num}.${subIdx} · CROSS VALIDATION`,
- title: sub,
- subtitle: '王总要求:"找到竞品没满足的 + 我们能满足的机会"',
- color: c.color,
- })}
- ${renderCompareTable({
- title: '竞品差评痛点 × 乳酸菌素片应答能力',
- headers: ['竞品痛点', 'VOC 数量', '我们的应答', '匹配度'],
- rows: painStats.map(ps => [
- { html: `<strong>${esc(ps.pain)}</strong>`, bold: true },
- `${ps.count} 条`,
- ps.ourAnswer,
- { html: `<strong style="color:${ps.score >= 85 ? BRAND.JIANG_GREEN : ps.score >= 70 ? BRAND.WARM_ORANGE : BRAND.CORAL};">${ps.score}%</strong>`, color: ps.score >= 85 ? BRAND.JIANG_GREEN : BRAND.WARM_ORANGE },
- ]),
- accent: c.color,
- })}
- ${painEvidence.length > 0 ? `
- <div style="margin-top:24px;">
- <div class="text-mono text-xs" style="color:${BRAND.CORAL}; letter-spacing:0.15em; margin-bottom:14px; font-weight:700;">竞品差评原声 · 我们的机会窗口</div>
- ${renderVocGrid(painEvidence, { columns: 2, accent: BRAND.CORAL, compact: true })}
- </div>` : ''}
- ${renderFindingCard({
- icon: '🎯',
- title: '交叉验证结论:三大交叉机会确认',
- body: `
- <strong style="color:${BRAND.JIANG_GREEN};">① 喂药难 → 咀嚼片</strong>(匹配度 95%):竞品最大痛点,我们的最强解<br/>
- <strong style="color:${BRAND.JIANG_GREEN};">② 选择困难 → 清晰定位</strong>(匹配度 90%):市场太乱,需要"一句话说清楚"<br/>
- <strong style="color:${BRAND.JIANG_GREEN};">③ 效果争议 → OTC 背书</strong>(匹配度 85%):食品级产品被骂"智商税",药品身份是天然壁垒
- `,
- accent: c.color,
- })}
- </div>
- </section>`;
- }
- // ============================================================
- // 3.3 竞品正反面精简分析
- // ============================================================
- function renderSub3({ chapter: c, subIdx, sub, slideNum, slideTotal, data, meta }) {
- const items = data?.items || [];
- const posRe = /好|有效|管用|推荐|不错|信任|放心|方便|好吃|爱吃|靠谱|改善|见效|专业|权威|医生.*开/i;
- const negRe = /难喂|不吃|吐|抗拒|没用|没效|智商税|贵|不值|难保存|冷链|副作用|拉肚|不好|差|坑|难吃|不喝/i;
- const competitors = [
- { name: '妈咪爱', type: '药品·散剂', re: /妈咪爱/i, color: BRAND.CORAL },
- { name: '合生元益生菌', type: '食品·粉剂', re: /合生元/i, color: BRAND.JIANG_GREEN },
- { name: '万益蓝益生菌', type: '食品·粉/滴', re: /万益蓝/i, color: BRAND.SKY_BLUE },
- { name: '亿活', type: '药品·胶囊/散', re: /亿活|布拉氏/i, color: BRAND.PURPLE },
- ];
- const cpData = competitors.map((cp, idx) => {
- const cpItems = items.filter(it => cp.re.test((it.content || '') + ' ' + (it.keyword || '') + ' ' + (it.product || '')));
- const posItems = cpItems.filter(it => posRe.test(it.content || ''));
- const negItems = cpItems.filter(it => negRe.test(it.content || ''));
- const posEv = LA.getEvidence(posItems.filter(it => stripTags(it.content).length >= 25), { top: 1, seed: 330 + idx, minChars: 20 });
- const negEv = LA.getEvidence(negItems.filter(it => stripTags(it.content).length >= 25), { top: 1, seed: 340 + idx, minChars: 20 });
- return { ...cp, total: cpItems.length, posCount: posItems.length, negCount: negItems.length, posEv, negEv };
- });
- return `<section class="report-section" id="${c.key}-${subIdx}" data-tone="${c.tone}" data-slide-num="${slideNum}/${slideTotal}">
- <div class="section-inner">
- ${renderSectionHead({
- eyebrow: `${c.num}.${subIdx} · COMPETITOR PROS & CONS`,
- title: sub,
- subtitle: '会议要求:竞品"除了缺点还有哪些优点"——正反面全面分析,基于 VOC 真实评论提取。',
- color: c.color,
- })}
- <div style="display:grid; grid-template-columns:repeat(2, 1fr); gap:18px;">
- ${cpData.map(cp => `
- <div style="padding:22px; background:var(--bg-1); border:1px solid var(--border); border-top:3px solid ${cp.color}; border-radius:var(--radius-md);">
- <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:14px;">
- <div>
- <div class="text-mono text-xs" style="color:${cp.color}; font-weight:700;">${esc(cp.name)}</div>
- <div style="color:var(--text-3); font-size:0.72rem;">${esc(cp.type)} · VOC ${cp.total} 条</div>
- </div>
- <div style="font-size:0.68rem; color:var(--text-3);">
- <span style="color:${BRAND.JIANG_GREEN};">+${cp.posCount}</span> /
- <span style="color:${BRAND.CORAL};">-${cp.negCount}</span>
- </div>
- </div>
- <div style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
- <div>
- <div style="color:${BRAND.JIANG_GREEN}; font-size:0.74rem; font-weight:700; margin-bottom:6px;">✅ 正面 VOC</div>
- ${cp.posEv.length > 0
- ? cp.posEv.map(ev => `<div style="padding:8px; background:${BRAND.JIANG_GREEN}06; border-left:2px solid ${BRAND.JIANG_GREEN}; border-radius:3px; font-size:0.76rem; color:var(--text-1); line-height:1.4;">
- "${esc((ev.content || '').slice(0, 70))}"
- <div style="color:var(--text-3); font-size:0.58rem; margin-top:3px;">—${esc(ev.nickname || ev.platform)} · ❤️${ev.likes || 0}</div>
- </div>`).join('')
- : `<div style="color:var(--text-3); font-size:0.76rem;">无明确正面 VOC</div>`}
- </div>
- <div>
- <div style="color:${BRAND.CORAL}; font-size:0.74rem; font-weight:700; margin-bottom:6px;">❌ 负面 VOC</div>
- ${cp.negEv.length > 0
- ? cp.negEv.map(ev => `<div style="padding:8px; background:${BRAND.CORAL}06; border-left:2px solid ${BRAND.CORAL}; border-radius:3px; font-size:0.76rem; color:var(--text-1); line-height:1.4;">
- "${esc((ev.content || '').slice(0, 70))}"
- <div style="color:var(--text-3); font-size:0.58rem; margin-top:3px;">—${esc(ev.nickname || ev.platform)} · ❤️${ev.likes || 0}</div>
- </div>`).join('')
- : `<div style="color:var(--text-3); font-size:0.76rem;">无明确负面 VOC</div>`}
- </div>
- </div>
- </div>`).join('')}
- </div>
- ${renderInsightHero({
- eyebrow: 'COMPETITIVE GAP',
- title: '乳酸菌素片的竞争空位:药品中唯一的"好吃"选项',
- subtitle: `
- 药品阵营(妈咪爱/亿活/宝乐安):<strong style="color:${BRAND.CORAL};">有效但难吃/难喂</strong><br/>
- 食品阵营(合生元/万益蓝/inne):<strong style="color:${BRAND.CORAL};">好吃但"智商税"争议</strong><br/>
- 乳酸菌素片:<strong style="color:${BRAND.JIANG_GREEN};">OTC 药品信任 + 咀嚼片好吃 = 两端通吃</strong>
- `,
- color: c.color,
- })}
- </div>
- </section>`;
- }
- module.exports = { renderSub1, renderSub2, renderSub3 };
|