amz_review_analyse/voc_业务_2/template.html
OnesvmWhoops cb6692c0c0 新增 voc_业务_2 报告流水线,并完善聚类与词频模块。
包含 Persona 锚定聚类、LLM 报告生成、方法论文档及 .gitignore 更新,便于在 Gitee 独立部署。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 17:12:39 +08:00

407 lines
20 KiB
HTML
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.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{PRODUCT_NAME}} — VOC 深度分析报告 {{VERSION}}</title>
{{ECHARTS_SCRIPT}}
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif;
background: #f8f9fa; color: #1a1a1a; font-size: 14px; line-height: 1.65; }
.top-nav { position: sticky; top: 0; z-index: 100; background: rgba(255,255,255,0.97);
backdrop-filter: blur(8px); border-bottom: 1px solid #e5e7eb;
display: flex; align-items: center; padding: 0 20px; overflow-x: auto; }
.top-nav .layer-tag { padding: 4px 10px; font-size: 11px; font-weight: 600; border-radius: 999px;
margin: 0 6px 0 2px; white-space: nowrap; }
.tag-what { background: #dbeafe; color: #1d4ed8; }
.tag-why { background: #fef3c7; color: #92400e; }
.top-nav a { padding: 12px 14px; font-size: 12px; font-weight: 500; color: #555;
text-decoration: none; white-space: nowrap; border-bottom: 2px solid transparent; transition: color .15s; }
.top-nav a:hover { color: #2563eb; }
.page { max-width: 1140px; margin: 0 auto; padding: 36px 24px 72px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.grid-3 { display: grid; grid-template-columns: repeat(3,1fr); gap: 14px; }
.grid-4 { display: grid; grid-template-columns: repeat(4,1fr); gap: 14px; }
.grid-5 { display: grid; grid-template-columns: repeat(5,1fr); gap: 14px; }
.exec-summary { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; margin-bottom: 18px; }
.exec-summary h3 { font-size: 14px; margin-bottom: 8px; color: #0f172a; }
h1 { font-size: 24px; font-weight: 700; color: #0f172a; margin-bottom: 4px; }
.subtitle { font-size: 12px; color: #888; margin-bottom: 6px; }
.layer-header { display: flex; align-items: center; gap: 12px; margin: 40px 0 16px; }
.layer-badge { padding: 3px 14px; border-radius: 999px; font-size: 12px; font-weight: 700; letter-spacing: .04em; }
.badge-what { background: #2563eb; color: #fff; }
.badge-why { background: #d97706; color: #fff; }
h2 { font-size: 17px; font-weight: 700; color: #0f172a; }
h3 { font-size: 13px; font-weight: 600; color: #374151; margin-bottom: 8px; }
.divider { border: none; border-top: 1px solid #e5e7eb; margin: 32px 0; }
.note { font-size: 11px; color: #999; margin-bottom: 10px; line-height: 1.5; }
.kpi-card { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; }
.kpi-val { font-size: 28px; font-weight: 700; color: #0f172a; line-height: 1.1; }
.kpi-val.blue { color: #2563eb; }
.kpi-val.success { color: #16a34a; }
.kpi-val.warn { color: #d97706; }
.kpi-val.danger { color: #dc2626; }
.kpi-lbl { font-size: 12px; color: #666; margin-top: 4px; }
.card { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; margin-bottom: 12px; }
.card-header { padding: 10px 16px; background: #f9fafb; border-bottom: 1px solid #e5e7eb;
font-size: 13px; font-weight: 600; color: #374151;
display: flex; justify-content: space-between; align-items: center;
cursor: pointer; user-select: none; }
.card-header:hover { background: #f3f4f6; }
.card-body { padding: 14px 16px; }
.card.collapsed .card-body { display: none; }
.toggle-icon::after { content: '\25B2'; font-size: 10px; color: #9ca3af; margin-left: 8px; }
.card.collapsed .toggle-icon::after { content: '\25BC'; }
.chart-box { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; margin-bottom: 16px; }
.chart-h { height: 240px; }
.chart-h-md { height: 300px; }
.chart-h-lg { height: 360px; }
.chart-scroll { overflow-y: auto; overflow-x: hidden; border-radius: 6px; }
.chart-scroll-x { overflow-x: auto; overflow-y: hidden; }
.asin-appendix { margin-top: 12px; font-size: 13px; color: #374151; }
.asin-appendix summary { cursor: pointer; font-weight: 600; padding: 8px 0; }
.star-dist { color: #2563eb; cursor: help; font-size: 11px; }
.theme-mini-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-top: 14px; }
.theme-mini-box { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 10px 12px; }
.theme-mini-box h4 { font-size: 12px; font-weight: 600; color: #374151; margin-bottom: 6px; }
.theme-mini-chart { height: 200px; }
.matrix-table td { font-size: 12px; line-height: 1.55; }
.matrix-table td small { font-size: 11px; }
.footer-asin-details summary { cursor: pointer; color: #2563eb; }
.chart-cap { font-size: 11px; color: #999; text-align: center; margin-top: 6px; }
.callout { border-radius: 8px; padding: 12px 16px; margin-bottom: 14px; }
.callout-warn { background: #fffbeb; border-left: 4px solid #f59e0b; }
.callout-danger { background: #fef2f2; border-left: 4px solid #dc2626; }
.callout-info { background: #eff6ff; border-left: 4px solid #3b82f6; }
.callout-success { background: #f0fdf4; border-left: 4px solid #22c55e; }
.callout-title { font-weight: 600; font-size: 13px; margin-bottom: 3px; }
.callout-warn .callout-title { color: #92400e; }
.callout-danger .callout-title { color: #b91c1c; }
.callout-info .callout-title { color: #1d4ed8; }
.callout-success .callout-title { color: #15803d; }
.callout p { font-size: 13px; color: #444; }
.quote { background: #f3f4f6; border-radius: 6px; padding: 8px 12px;
font-size: 12px; font-style: italic; color: #555; line-height: 1.55; margin: 6px 0; }
.quote.neg { border-left: 3px solid #fca5a5; background: #fff5f5; }
.quote.pos { border-left: 3px solid #86efac; background: #f0fff4; }
.quote a { color: #2563eb; font-style: normal; text-decoration: none; }
.quote a:hover { text-decoration: underline; }
.quote-cn { font-size: 11px; color: #666; font-style: normal; margin-top: 4px; }
.quote-empty { font-size: 12px; color: #999; font-style: italic; }
.pill { display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px; }
.pill-danger { background: #fee2e2; color: #b91c1c; border: 1px solid #fca5a5; }
.pill-warn { background: #fef3c7; color: #92400e; border: 1px solid #fde68a; }
.pill-info { background: #dbeafe; color: #1d4ed8; }
.pill-success { background: #dcfce7; color: #15803d; }
.pill-gray { background: #f3f4f6; color: #6b7280; }
.tbl-wrap { overflow-x: auto; margin-bottom: 16px; }
table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 8px;
overflow: hidden; border: 1px solid #e5e7eb; font-size: 13px; }
thead { background: #f3f4f6; }
th { padding: 9px 12px; text-align: left; font-size: 11px; font-weight: 600; color: #555;
border-bottom: 1px solid #e5e7eb; white-space: nowrap; }
td { padding: 9px 12px; border-bottom: 1px solid #f3f4f6; vertical-align: top; }
tr:last-child td { border-bottom: none; }
td a { color: #2563eb; text-decoration: none; }
td a:hover { text-decoration: underline; }
tr.row-r td { background: #fff5f5; }
tr.row-y td { background: #fffdf0; }
tr.row-g td { background: #f0fff4; }
tr.row-b td { background: #eff6ff; }
.num { text-align: right; }
.persona { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; }
.p-name { font-weight: 700; font-size: 14px; margin-bottom: 2px; }
.p-meta { font-size: 11px; color: #888; margin-bottom: 8px; }
.p-pct { font-weight: 700; color: #2563eb; }
.p-row { font-size: 12px; margin: 4px 0; color: #444; }
.p-label { font-weight: 600; color: #374151; }
.sat-hi { color: #16a34a; font-weight: 700; }
.sat-mid { color: #d97706; font-weight: 700; }
.sat-lo { color: #dc2626; font-weight: 700; }
.rc-section { margin-bottom: 14px; }
.rc-label { font-size: 12px; font-weight: 700; color: #374151; margin: 10px 0 4px; }
.rc-text { font-size: 13px; color: #444; line-height: 1.65; }
.jtbd-cell { max-width: 180px; font-size: 12px; line-height: 1.5; }
.kano-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; margin-bottom: 20px; }
.kano-card { border-radius: 8px; padding: 14px; border: 1px solid; }
.kano-basic { background: #fef2f2; border-color: #fca5a5; }
.kano-perf { background: #fffbeb; border-color: #fde68a; }
.kano-excite { background: #f0fdf4; border-color: #86efac; }
.kano-rev { background: #f5f3ff; border-color: #c4b5fd; }
.kano-label { font-size: 11px; font-weight: 700; letter-spacing: .06em; margin-bottom: 6px; text-transform: uppercase; }
.kano-basic .kano-label { color: #b91c1c; }
.kano-perf .kano-label { color: #92400e; }
.kano-excite .kano-label { color: #15803d; }
.kano-rev .kano-label { color: #6d28d9; }
.kano-title { font-size: 13px; font-weight: 700; margin-bottom: 8px; color: #111; }
.kano-item { font-size: 12px; padding: 6px 0; border-bottom: 1px solid rgba(0,0,0,.05); display: grid; grid-template-columns: auto 1fr; gap: 8px; }
.kano-item:last-child { border-bottom: none; }
.kano-icon { font-size: 14px; margin-top: 1px; line-height: 1.4; }
.kano-fields { display: flex; flex-direction: column; gap: 2px; }
.kf-name { font-weight: 600; color: #111; font-size: 12px; }
.kf-row { font-size: 11px; color: #555; line-height: 1.5; }
.kf-key { font-weight: 600; color: #374151; }
.kano-empty { font-size: 12px; color: #888; padding: 8px 0; }
.danger { color: #dc2626; font-weight: 700; }
.warn { color: #d97706; font-weight: 700; }
.footer { font-size: 11px; color: #aaa; margin-top: 48px; padding-top: 14px; border-top: 1px solid #e5e7eb; }
@media (max-width: 900px) {
.grid-2,.grid-3,.grid-4,.grid-5,.kano-grid { grid-template-columns: 1fr; }
.theme-mini-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<nav class="top-nav">
<span class="layer-tag tag-what">描述层</span>
<a href="#sec-summary">决策摘要</a>
<a href="#sec-overview">数据总览</a>
<a href="#sec-asin-theme">ASIN主题</a>
<a href="#sec-persona">受众画像</a>
<a href="#sec-feedback">正负反馈</a>
<a href="#sec-keyword">情感词频</a>
<span class="layer-tag tag-why">分析层</span>
<a href="#sec-kano">KANO 模型</a>
<a href="#sec-jtbd">JTBD 动机</a>
<a href="#sec-matrix">人群矩阵</a>
<a href="#sec-rootcause">痛点根因</a>
</nav>
<div class="page">
<h1>{{PRODUCT_NAME}} — VOC 深度分析报告</h1>
<p class="subtitle">{{ASIN_COUNT}} 个竞品 ASIN · 数据来源:{{DATA_SOURCE}} · 分析日期:{{ANALYSIS_DATE}}</p>
<div class="callout callout-info" style="margin-top:14px">
<div class="callout-title">报告阅读指引</div>
<p><strong>描述层 What</strong>:先看「决策摘要」→ 数据总览 → 各 ASIN 主题对比 → 受众画像 → 正负反馈主题 → 情感词频<br>
<strong>分析层 Why</strong>:解释动机根因(KANO 需求分层 → JTBD 动机框架 → 人群×场景×需求矩阵 → 痛点根因分析)</p>
</div>
<div class="exec-summary" id="sec-summary">
<h3>决策摘要</h3>
{{EXEC_SUMMARY_HTML}}
</div>
{{INSIGHTS_CALLOUT}}
<!-- ══ §1 数据总览 ══ -->
<div class="layer-header" id="sec-overview">
<span class="layer-badge badge-what">描述层 What</span>
<h2>数据总览</h2>
</div>
<div class="callout callout-{{MARKET_CALLOUT_TYPE}}">
<div class="callout-title">市场竞争状态:{{MARKET_TITLE}}</div>
<p>{{MARKET_DESC}}</p>
</div>
<div class="grid-5" style="margin-bottom:22px">
<div class="kpi-card"><div class="kpi-val blue">{{TOTAL_REVIEWS}}</div><div class="kpi-lbl">有效评论总数(Verified+Vine)</div></div>
<div class="kpi-card"><div class="kpi-val {{AVG_COLOR}}">{{WEIGHTED_AVG}}</div><div class="kpi-lbl">{{ASIN_COUNT}}款加权平均评分</div></div>
<div class="kpi-card"><div class="kpi-val success">{{POS_RATE}}</div><div class="kpi-lbl">正评率(≥4★)</div></div>
<div class="kpi-card"><div class="kpi-val warn">{{NEUTRAL_RATE}}</div><div class="kpi-lbl">中评率(3★,{{NEUTRAL_COUNT}}条)</div></div>
<div class="kpi-card"><div class="kpi-val danger">{{NEG_RATE}}</div><div class="kpi-lbl">差评率(≤2★)</div></div>
</div>
<h3>各 ASIN 有效评论统计</h3>
{{ASIN_TABLE_NOTE}}
<div class="tbl-wrap">{{ASIN_TABLE_SUMMARY}}</div>
{{ASIN_TABLE_APPENDIX}}
<div class="chart-box">
{{STAR_SUMMARY_HTML}}
<h3>各 ASIN 评分分布(堆叠)</h3>
<p class="note">{{STAR_CHART_NOTE}}</p>
<div class="chart-scroll" style="max-height:{{STAR_SCROLL_MAX}}px">
<div id="starDistChart" style="width:100%;height:{{STAR_CHART_HEIGHT}}px"></div>
</div>
<p class="chart-cap">数据来源:{{DATA_SOURCE}} · 筛选 verified=True 或 vine=True · 轴标签 A/B/… 悬停图表查看完整竞品名</p>
</div>
<hr class="divider">
<!-- ══ §1b 各 ASIN 主题分布 ══ -->
<div class="layer-header" id="sec-asin-theme">
<span class="layer-badge badge-what">描述层 What</span>
<h2>各 ASIN 主题分布</h2>
</div>
<p class="note">横向对比各竞品在 Top 差评/好评主题上的评论命中数,识别弱点集中 ASIN 与卖点组合差异。</p>
{{ASIN_THEME_INSIGHTS_HTML}}
<div class="chart-box">
<h3>各 ASIN 差评主题对比(Top 6 主题 × {{ASIN_COUNT}} 竞品)</h3>
<p class="note">{{NEG_THEME_CHART_NOTE}}</p>
<div id="negChartPerAsin" style="width:100%;height:{{NEG_THEME_CHART_HEIGHT}}px"></div>
{{NEG_THEME_MINI_HTML}}
</div>
<div class="chart-box">
<h3>各 ASIN 好评主题对比(Top 6 主题 × {{ASIN_COUNT}} 竞品)</h3>
<p class="note">{{POS_THEME_CHART_NOTE}}</p>
<div id="posChartPerAsin" style="width:100%;height:{{POS_THEME_CHART_HEIGHT}}px"></div>
{{POS_THEME_MINI_HTML}}
</div>
<hr class="divider">
<!-- ══ §2 受众画像 ══ -->
<div class="layer-header" id="sec-persona">
<span class="layer-badge badge-what">描述层 What</span>
<h2>用户画像(Persona)</h2>
</div>
<div class="grid-2">{{PERSONA_CARDS}}</div>
<hr class="divider">
<!-- ══ §3 正负反馈 ══ -->
<div class="layer-header" id="sec-feedback">
<span class="layer-badge badge-what">描述层 What</span>
<h2>正负反馈主题统计</h2>
</div>
<p class="note">{{NEG_THEME_SUMMARY_NOTE}}</p>
<div class="grid-2">
<div class="chart-box">
<h3>差评主题频次</h3>
<div class="chart-h-lg" id="negChart"></div>
</div>
<div class="chart-box">
<h3>好评主题频次(≥4 星,共 {{POS_REVIEW_COUNT}} 条)</h3>
<div class="chart-h-lg" id="posChart"></div>
</div>
</div>
<h3>差评主题明细</h3>
<div class="tbl-wrap">
<table>
<thead><tr><th>差评主题(按根因拆分,不合并)</th><th class="num">频次</th><th class="num">占差评比</th><th>优先级</th><th>涉及范围&amp;最痛 ASIN</th></tr></thead>
<tbody>{{NEG_THEME_TABLE_ROWS}}</tbody>
</table>
</div>
<h3>好评主题明细</h3>
<p class="note">占好评比基于单主题命中计数;同一评论可命中多个主题,各行占比之和可能超过 100%(当前合计约 {{POS_PCT_SUM}}%)。KANO 预判为 What 层摘要,详细分类见下方 KANO 模型章节。</p>
<div class="tbl-wrap">
<table>
<thead><tr><th>好评主题</th><th class="num">频次</th><th class="num">占好评比</th><th>KANO 预判</th></tr></thead>
<tbody>{{POS_THEME_TABLE_ROWS}}</tbody>
</table>
</div>
<hr class="divider">
<!-- ══ §4 情感词频 ══ -->
<div class="layer-header" id="sec-keyword">
<span class="layer-badge badge-what">描述层 What</span>
<h2>情感关键词分析</h2>
</div>
<p class="note">规则:同一评论中同一词组多次出现计 1 次;极性根据评论语境判断;含义只写评论中明确出现的内容,不推断。</p>
<div class="grid-2">
<div>
<h3 style="font-size:14px;margin:0 0 8px">高频负面情感词(差评,≤2★)</h3>
<div class="tbl-wrap">
<table>
<thead><tr><th>词汇</th><th class="num">频次</th><th>情感极性</th><th>评论中使用语境</th><th>主要关联 Persona</th></tr></thead>
<tbody>{{KEYWORD_NEG_TABLE_ROWS}}</tbody>
</table>
</div>
</div>
<div>
<h3 style="font-size:14px;margin:0 0 8px">高频正面情感词(好评,≥4★)</h3>
<div class="tbl-wrap">
<table>
<thead><tr><th>词汇</th><th class="num">频次</th><th>情感极性</th><th>评论中使用语境</th><th>主要关联 Persona</th></tr></thead>
<tbody>{{KEYWORD_POS_TABLE_ROWS}}</tbody>
</table>
</div>
</div>
</div>
<hr class="divider">
<!-- ══ §5 KANO ══ -->
<div class="layer-header" id="sec-kano">
<span class="layer-badge badge-why">分析层 Why</span>
<h2>KANO 模型需求分类</h2>
</div>
<p class="note">每个需求条目包含:需求项 · 频次证据 · 主要影响 Persona · 分类原因 · 竞品现状(5 字段,按 SOP 要求)</p>
{{KANO_GRID_HTML}}
<hr class="divider">
<!-- ══ §6 JTBD ══ -->
<div class="layer-header" id="sec-jtbd">
<span class="layer-badge badge-why">分析层 Why</span>
<h2>JTBD 动机框架</h2>
</div>
<p class="note">各动机字段须来自 Persona 聚类数据(keywords / core_pain / core_need 等)佐证;无法找到佐证的字段填「-」。</p>
<div class="tbl-wrap">
<table>
<thead><tr><th>用户群</th><th>核心 Job</th><th>功能性动机</th><th>情感性动机</th><th>社会性动机</th><th>购买触发时机</th></tr></thead>
<tbody>{{JTBD_TABLE_ROWS}}</tbody>
</table>
</div>
<hr class="divider">
<!-- ══ §7 人群矩阵 ══ -->
<div class="layer-header" id="sec-matrix">
<span class="layer-badge badge-why">分析层 Why</span>
<h2>人群 × 场景 × 需求矩阵</h2>
</div>
<p class="note">场景字段来源:评论中出现次数 ≥5 的场景词方可填写;每 Persona 最多 2 个场景、全报告最多 4 个 Persona。全市场均分偏低时,「高满意度」会自动校准为中等并注明原因。</p>
<div class="tbl-wrap">
<table class="matrix-table">
<thead>
<tr>
<th style="min-width:130px">用户群</th>
<th style="min-width:150px">使用场景(When/Where)<br><small style="font-weight:normal;color:#aaa">仅评论中佐证≥5条的场景</small></th>
<th style="min-width:150px">基本型需求</th>
<th style="min-width:150px">期望型需求</th>
<th style="min-width:120px">魅力型需求</th>
<th style="min-width:100px">当前满意度</th>
</tr>
</thead>
<tbody>{{MATRIX_TABLE_ROWS}}</tbody>
</table>
</div>
<hr class="divider">
<!-- ══ §8 痛点根因 ══ -->
<div class="layer-header" id="sec-rootcause">
<span class="layer-badge badge-why">分析层 Why</span>
<h2>痛点根因分析(按 Persona 展开)</h2>
</div>
{{ROOTCAUSE_CARDS}}
<div class="footer">
<strong>数据来源</strong>:{{DATA_SOURCE}}({{ASIN_COUNT}} 个 ASIN)<br>
<strong>ASIN</strong>:{{FOOTER_ASIN_LINKS}}<br>
<strong>筛选规则</strong>:仅统计 verified=True 或 vine=True 的有效评论,共 {{TOTAL_REVIEWS}} 条<br>
<strong>分析框架</strong>:VOC 数据清洗、分析与报告生成通用方法论 v1.0 · <strong>分析截止日期</strong>:{{ANALYSIS_DATE}}
</div>
</div>
<script>
function toggleCard(header) {
header.parentElement.classList.toggle('collapsed');
}
// ── ECharts 图表 ──
{{CHART_JS}}
</script>
</body>
</html>