|
|
@@ -82,10 +82,77 @@ for (const r of items) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 噪声识别
|
|
|
-const noiseRe = /互赞|互助|加油.*互|可以.*点赞|点赞.*返|蹲.*搭子/;
|
|
|
-const noiseComments = allComments.filter((c) => noiseRe.test(c.content));
|
|
|
-const realComments = allComments.filter((c) => !noiseRe.test(c.content));
|
|
|
+// ============== 噪声识别 + 真实评论筛选 v4 ==============
|
|
|
+const NOISE_RE = /互赞|互助|加油.*互|可以.*点赞|点赞.*返|蹲.*搭子|互粉/;
|
|
|
+const OFFTOPIC_RE = /眼影|口红|睫毛|化妆|分装|借我|借用|护肤|穿搭|开箱|美术生|画的|画得|插画|手工/;
|
|
|
+const PROMO_RE = /推广|求关注|求互|私信|加微|商务合作|接广告|涨粉/;
|
|
|
+const SHILL_RE = /宝子|亲测|无套路|必.{0,2}冲|绝绝子|拉满|薅羊毛|放心冲|闭眼冲|爆棚|颜值直接|姐妹.{0,3}冲|实力宠粉|爆款|0元购|yyds|YYDS|惊艳到|跺脚/;
|
|
|
+const META_KOL_RE = /博主|up主|UP主|接广|涨粉|掉粉|播放/;
|
|
|
+const EMOJI_HEAVY_RE = /(🥬|🥢|🌿|🤩|💯|🔥|💥|✅|💦|📸|🥳|🛒|🎉|✨|💚|❗|‼️)/g;
|
|
|
+const stripDeco = (s) => s.replace(/\[[^\]]+\]/g, '').replace(/[@#][^\s@#]+/g, '').replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '');
|
|
|
+
|
|
|
+const noiseComments = allComments.filter((c) => NOISE_RE.test(c.content));
|
|
|
+
|
|
|
+// 跑步机产品强相关词典 - 7 大主题
|
|
|
+const PRODUCT_TOPICS_SHUHUA = {
|
|
|
+ '静音减震': /静音|减震|噪音|噪声|声音|分贝|楼下|楼上|邻居|扰邻|商品房|楼板|响|吵|安静/,
|
|
|
+ '专业参数': /坡度|速度|心率|配速|马达|马力|功率|参数|配置|续航|履带|跑带|尺寸/,
|
|
|
+ '场景体验': /家用|居家|社恐|健身房|减肥|减脂|塑形|课程|训练|跑步|走路|快走/,
|
|
|
+ '购买决策': /购买|入手|考虑|对比|划算|性价比|价格|预算|犹豫|推荐|值得|后悔/,
|
|
|
+ '使用反馈': /好用|不好用|质量|耐用|结实|稳|抖|坏了|售后|故障|包装|物流|安装/,
|
|
|
+ '竞品提及': /易跑|麦瑞克|亿健|岱宇|舒华|小米|华为|keep|Keep/,
|
|
|
+ '复购口碑': /用了.*[年月天]|每天|经常|坚持|续费|又买|回头|二次/,
|
|
|
+};
|
|
|
+const PRODUCT_RE_SHUHUA = new RegExp(Object.values(PRODUCT_TOPICS_SHUHUA).map((r) => r.source).join('|'));
|
|
|
+
|
|
|
+const realCommentsAll = allComments.filter((c) => {
|
|
|
+ const txt = c.content || '';
|
|
|
+ if (c.nick && c.koc && c.nick === c.koc) return false; // 博主自评
|
|
|
+ if (/^\s*@/.test(txt) && stripDeco(txt.replace(/^\s*@[^\s@#]+/, '')).length < 20) return false;
|
|
|
+ if (NOISE_RE.test(txt) || OFFTOPIC_RE.test(txt) || PROMO_RE.test(txt) || SHILL_RE.test(txt) || META_KOL_RE.test(txt)) return false;
|
|
|
+ const emojis = txt.match(EMOJI_HEAVY_RE) || [];
|
|
|
+ if (emojis.length >= 3) return false;
|
|
|
+ const core = stripDeco(txt);
|
|
|
+ if (core.length < 6) return false;
|
|
|
+ if (core.length > 80 && emojis.length >= 1) return false;
|
|
|
+ if (core.length > 120) return false;
|
|
|
+ return true;
|
|
|
+});
|
|
|
+// 产品强相关优先,但样本小不强制 (允许后补)
|
|
|
+const realComments = realCommentsAll.filter((c) => PRODUCT_RE_SHUHUA.test(c.content));
|
|
|
+const realCommentsFallback = realCommentsAll.filter((c) => !PRODUCT_RE_SHUHUA.test(c.content));
|
|
|
+
|
|
|
+// 主题分桶 + 信息量排序 + 轮询
|
|
|
+const informationScore = (c) => {
|
|
|
+ let hits = 0;
|
|
|
+ for (const re of Object.values(PRODUCT_TOPICS_SHUHUA)) if (re.test(c.content)) hits++;
|
|
|
+ return hits * 20 + stripDeco(c.content).length;
|
|
|
+};
|
|
|
+const buckets = {};
|
|
|
+for (const tag of Object.keys(PRODUCT_TOPICS_SHUHUA)) buckets[tag] = [];
|
|
|
+for (const c of realComments) {
|
|
|
+ for (const [tag, re] of Object.entries(PRODUCT_TOPICS_SHUHUA)) {
|
|
|
+ if (re.test(c.content)) { buckets[tag].push(c); break; }
|
|
|
+ }
|
|
|
+}
|
|
|
+for (const tag of Object.keys(buckets)) buckets[tag].sort((a, b) => (b.like - a.like) || (informationScore(b) - informationScore(a)));
|
|
|
+
|
|
|
+const topPicks = [];
|
|
|
+const queues = Object.entries(buckets).map(([tag, list]) => ({ tag, list, idx: 0 }));
|
|
|
+for (let round = 0; round < 4 && topPicks.length < 12; round++) {
|
|
|
+ for (const q of queues) {
|
|
|
+ if (topPicks.length >= 12) break;
|
|
|
+ if (q.idx < q.list.length && q.idx < round + 1) {
|
|
|
+ topPicks.push({ topic: q.tag, c: q.list[q.idx] });
|
|
|
+ q.idx++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+// 不够 12 条用 fallback 补
|
|
|
+for (const c of realCommentsFallback.sort((a, b) => b.like - a.like)) {
|
|
|
+ if (topPicks.length >= 12) break;
|
|
|
+ topPicks.push({ topic: '其他真实声音', c });
|
|
|
+}
|
|
|
|
|
|
// ============== 3. 话题主题分析 ==============
|
|
|
const TOPICS = [
|
|
|
@@ -185,28 +252,227 @@ const RECS = [
|
|
|
|
|
|
// ============== 5.5 新机会矩阵 (基于评论逐条逆向挖掘) ==============
|
|
|
const OPPORTUNITIES = [
|
|
|
- // A. 产品/卖点
|
|
|
- { id: 'A1', cat: 'A · 产品卖点', p: 'P0', t: '静音减震 RTB 具象化', signal: '沐阳 3 连评论 · 商品房+楼板+邻居', action: '分贝评测 + 楼板自测指南 + 真实邻居访谈视频三件套,把"静音减震"从形容词变成可信证据' },
|
|
|
- { id: 'A2', cat: 'A · 产品卖点', p: 'P0', t: '坡度参数明牌对比', signal: '木塔塔晒坡度15-20 + 星星不懂星星推易跑"-6到26"', action: '简报必出"舒华 vs 易跑参数对比表",拦截"易跑"搜索流量,直接挖竞品墙脚' },
|
|
|
- { id: 'A3', cat: 'A · 产品卖点', p: 'P0', t: '社恐居家健身专属内容线', signal: '星星不懂星星/福建: 社恐+不爱挤健身房', action: '5-10 篇专项内容锁定 i 人/社恐/独居场景,占领被易跑先占的细分心智' },
|
|
|
- { id: 'A4', cat: 'A · 产品卖点', p: 'P1', t: '跑步机 × 课程包绑定', signal: '兔子河南: "第一节课/回放课"', action: '联名 Keep / 帕梅拉 / 刘畊宏,买机送 1 年课程 VIP,客单价翻 2-3 倍' },
|
|
|
- // B. 内容形态
|
|
|
- { id: 'B1', cat: 'B · 内容形态', p: 'P1', t: '365 天蜕变长期 IP', signal: '兔子湖南: 一年前奶茶不离手 → 一年后敢露肉了"换头"', action: '招募 100 位真实用户做 1 年期 KOL,送机 + 月度回访,产出对比照,双 11/618 集中释放' },
|
|
|
- { id: 'B2', cat: 'B · 内容形态', p: 'P0', t: 'UGC 参数交流模板', signal: '木塔塔: 坡度15-20/速度4.2-4.5/心率135-149', action: '简报加入"晒你的训练参数"话题,App 自动生成参数分享卡,自然形成 UGC 素材库' },
|
|
|
- { id: 'B3', cat: 'B · 内容形态', p: 'P0', t: '泛流量型升级为专业体验型', signal: '形态错配反向信号: 泛流量型互动最低但产生最高质量评论', action: '把"泛流量相关型"重命名为"专业体验交流型",下一轮投入比例从 12% 提到 30%' },
|
|
|
- { id: 'B4', cat: 'B · 内容形态', p: 'P1', t: '真实邻居访谈视频', signal: '沐阳云南: 跑步机声 + 邻居反馈"没听到"', action: '找 5 位老用户上门,让楼下邻居出镜确认,这是 KOL 软文做不到的真实信任' },
|
|
|
- // C. 人群渠道
|
|
|
- { id: 'C1', cat: 'C · 人群渠道', p: 'P0', t: 'KOC 性别/年龄结构调整', signal: '本期 43 位 KOC 全为萌系女性昵称,跑步机决策人群错位', action: '男性 30% + 宝爸宝妈 20% + 30+ 中产女性 10%,等于换一批用户' },
|
|
|
- { id: 'C2', cat: 'C · 人群渠道', p: 'P1', t: '商品房新业主精准包', signal: '沐阳: "商品房 / 开发商问题"', action: '定向 3 年内交付的高层公寓用户,这是扰邻焦虑最强的目标人群' },
|
|
|
- { id: 'C3', cat: 'C · 人群渠道', p: 'P1', t: '减肥搭子社交营 (噪声资产化)', signal: '47% 互助互赞噪声实为真实减肥群体寻找搭子', action: '#舒华 30 天减肥搭子营 官方话题 + 企微私域 + 月度 PK 赛事,把噪声变长效资产' },
|
|
|
- { id: 'C4', cat: 'C · 人群渠道', p: 'P0', t: '一线 + 新一线 IP 占比 ≥ 50%', signal: '本期评论 IP 缺京沪深广杭蓉', action: '跑步机是大件家居,一线城市才有支付力 + 居住空间,选号必须明确 IP 配比' },
|
|
|
- // D. 投放机制
|
|
|
- { id: 'D1', cat: 'D · 投放机制', p: 'P0', t: '类目标签从家居/好物迁到健身/减脂', signal: '本期挂"家居 22 + 好物 21",但搜索词是"跑步机推荐/家用跑步机"', action: '简报锁定 SEO 标签 居家健身 / 减脂塑形 / 跑步机推荐 / 健身房替代' },
|
|
|
- { id: 'D2', cat: 'D · 投放机制', p: 'P1', t: 'MCN 来源审计 + 多源选号', signal: '粉丝量 1k-2k 高度同质,疑似单一 MCN 包号', action: '≥ 3 家 MCN + ≥ 20% 蒲公英直选 + ≥ 10% 自建池 + 粉丝阶梯化金字塔结构' },
|
|
|
- { id: 'D3', cat: 'D · 投放机制', p: 'P2', t: '7 天上门试驾 O2O', signal: '星星不懂星星山东: "免费体验欢迎来锻炼"', action: '借鉴戴森/特斯拉,跑步机 7 天上门 + 不满意退款,降低决策门槛,KOC 矩阵推广' },
|
|
|
- // E. 数据机制
|
|
|
- { id: 'E1', cat: 'E · 数据机制', p: 'P0', t: '投后 24h 抓取 + 博主行为预警', signal: 'xlsx 19 评论 → API 17, 缺失 2 条 (可能博主自删)', action: '24h 内首版数据快照,捕捉删评/删笔记信号,识别评论区翻车风险' },
|
|
|
- { id: 'E2', cat: 'E · 数据机制', p: 'P1', t: '3 天数据保护期 + 死笔记自动补流', signal: '11/43 篇互动 < 5,33/43 篇产出极差 = ¥7000+ 浪费', action: '投后 3 天数据 < 阈值的笔记,自动触发 ¥30-50 薯条二次投流补救,而非直接放弃' },
|
|
|
+ {
|
|
|
+ id: 'A1', cat: 'A · 产品卖点', p: 'P0', t: '静音减震 RTB 具象化',
|
|
|
+ signal: '沐阳 3 连评论 · 商品房+楼板+邻居',
|
|
|
+ action: '分贝评测 + 楼板自测指南 + 真实邻居访谈视频三件套,把"静音减震"从形容词变成可信证据',
|
|
|
+ why: '"扰邻焦虑"是商品房 / 高层住宅用户购买跑步机最大决策门槛。当前 KOC 笔记停留在"很安静"这种泛形容词,用户依然不敢下单。把抽象承诺翻译成"分贝数 / 楼下邻居反馈视频 / 楼板震动测试"等可见可量化证据,直接拆掉决策最后一道墙。',
|
|
|
+ how: [
|
|
|
+ '分贝评测视频: 不同档位实测 + 同房间对比 (跑步 vs 洗衣机 vs 电视)',
|
|
|
+ '楼板自测指南: 1 张图教用户在自家楼板放杯水/硬币测震动',
|
|
|
+ '真实邻居访谈视频: 找 5 位老用户上门拍楼下邻居反馈 (KOL 软文做不到)',
|
|
|
+ '官方素材包统一分发,KOC 简报硬性引用 1 个静音 RTB 镜头',
|
|
|
+ ],
|
|
|
+ outcome: '"扰邻焦虑"评论占比下降 50%,商品房用户转化率 +20-30%,客户咨询从"声音多大"转为"什么时候发货"',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'A2', cat: 'A · 产品卖点', p: 'P0', t: '坡度参数明牌对比',
|
|
|
+ signal: '木塔塔晒坡度 15-20 + 星星不懂星星推易跑"-6 到 26"',
|
|
|
+ action: '简报必出"舒华 vs 易跑参数对比表",拦截"易跑"搜索流量,直接挖竞品墙脚',
|
|
|
+ why: '17 条评论里"易跑"被自然提及 1 次 = 用户已在对比阶段。健身硬核用户决策依赖参数,但本期 KOC 笔记几乎不晒参数,白白把这部分流量让给了竞品。明牌参数对比 = 拦截"易跑"搜索流量 + 锁定专业人群。',
|
|
|
+ how: [
|
|
|
+ '简报硬性出"舒华 vs 易跑参数对比表" (坡度/速度/马达/承重/降噪)',
|
|
|
+ 'SEO 关键词加投: "易跑跑步机对比" / "易跑值得买吗"',
|
|
|
+ '专业 KOL 1-2 篇深度评测,拍真实使用场景',
|
|
|
+ '在评论区主动应战: 用户提竞品时官方账号给数据回应',
|
|
|
+ ],
|
|
|
+ outcome: '拦截 "易跑" 搜索流量 30%,专业用户决策周期 -50%,品牌专业度认知从 60 → 80 分',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'A3', cat: 'A · 产品卖点', p: 'P0', t: '社恐居家健身专属内容线',
|
|
|
+ signal: '星星不懂星星/福建: 社恐+不爱挤健身房',
|
|
|
+ action: '5-10 篇专项内容锁定 i 人/社恐/独居场景,占领被易跑先占的细分心智',
|
|
|
+ why: '"社恐 / i 人 / 不去健身房" 是 2024-2025 小红书最大居家心智之一。竞品易跑已抢先占位,但市场远未饱和。舒华本身静音 + 折叠属性天然适合居家,只是没有把这个心智专门讲透。',
|
|
|
+ how: [
|
|
|
+ '5-10 篇专项笔记: 主题"社恐如何在家瘦 30 斤"/"i 人居家健身 30 天"',
|
|
|
+ 'KOL 选号偏向"自我表达型 i 人"达人 (粉丝 5k-50k 即可)',
|
|
|
+ '内容必含: 不开摄像头 / 不和教练社交 / 自己节奏 3 个心智锚点',
|
|
|
+ '配套 SOP: 跑步机自动连接耳机 / App 一键开始训练',
|
|
|
+ ],
|
|
|
+ outcome: '社恐/居家细分心智份额建立,从竞品手里挖走 10-15% 用户',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'A4', cat: 'A · 产品卖点', p: 'P1', t: '跑步机 × 课程包绑定',
|
|
|
+ signal: '兔子河南: "第一节课/回放课"',
|
|
|
+ action: '联名 Keep / 帕梅拉 / 刘畊宏,买机送 1 年课程 VIP,客单价翻 2-3 倍',
|
|
|
+ why: '跑步机是"硬件思维",但用户需要的是"训练效果"。捆绑顶流课程包 = 用软件价值溢价硬件,客单价从 2k 翻到 5-6k 不会让用户感到贵,反而觉得"超值"。',
|
|
|
+ how: [
|
|
|
+ '与 Keep / 帕梅拉 / 刘畊宏 1 家以上签深度合作',
|
|
|
+ '买机送 1 年课程 VIP (单买价值 ¥600-1000)',
|
|
|
+ '入门款机型搭课程版本独立 SKU,锁定首单转化',
|
|
|
+ '蒲公英 KOC 简报必引用"机器+课程"组合卖点',
|
|
|
+ ],
|
|
|
+ outcome: '客单价 ×2-3,首单转化率 +30%,差异化壁垒 (硬件竞品难以复刻)',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'B1', cat: 'B · 内容形态', p: 'P1', t: '365 天蜕变长期 IP',
|
|
|
+ signal: '兔子湖南: 一年前奶茶不离手 → 一年后敢露肉了"换头"',
|
|
|
+ action: '招募 100 位真实用户做 1 年期 KOL,送机 + 月度回访,产出对比照,双 11/618 集中释放',
|
|
|
+ why: '"用户真实蜕变" 是跑步机最强转化素材,1 张前后对比照 > 100 篇 KOC 探店。但这种内容需要时间沉淀,不能一次性买。先种树,1 年后大丰收。',
|
|
|
+ how: [
|
|
|
+ '招募 100 位真实用户做 1 年期 IP (粉丝 1k-50k 不限)',
|
|
|
+ '免费送机 + 月度回访,内容创作支持',
|
|
|
+ '统一话题 #舒华 365 天蜕变,品牌官方账号集合分发',
|
|
|
+ '双 11/618 节点集中释放对比照,做爆款营销',
|
|
|
+ ],
|
|
|
+ outcome: '1 年后形成 100 个真实蜕变案例,每节点节省广告费 50% 以上,UGC 持续渗透长尾',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'B2', cat: 'B · 内容形态', p: 'P0', t: 'UGC 参数交流模板',
|
|
|
+ signal: '木塔塔: 坡度 15-20/速度 4.2-4.5/心率 135-149',
|
|
|
+ action: '简报加入"晒你的训练参数"话题,App 自动生成参数分享卡,自然形成 UGC 素材库',
|
|
|
+ why: '"参数交流" 在跑步机社群是最自然的 UGC 形态,但目前几乎没有引导。一条用户晒参数的笔记 = 1 个免费 RTB 锚点,且能引发其他用户参数对比讨论。',
|
|
|
+ how: [
|
|
|
+ '简报加入"晒你的训练参数"话题,作为加分项',
|
|
|
+ 'App 端自动生成训练参数分享卡 (1 键导出图片)',
|
|
|
+ '官方账号每周精选"本周最强参数"专题',
|
|
|
+ '建立训练参数排行榜,激励用户晒数据',
|
|
|
+ ],
|
|
|
+ outcome: '免费 UGC 素材库 +50 条/月,品牌专业度持续渗透,长尾 SEO 流量 +20%',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'B3', cat: 'B · 内容形态', p: 'P0', t: '泛流量型升级为专业体验型',
|
|
|
+ signal: '形态错配反向信号: 泛流量型互动最低但产生最高质量评论',
|
|
|
+ action: '把"泛流量相关型"重命名为"专业体验交流型",下一轮投入比例从 12% 提到 30%',
|
|
|
+ why: '本期最大反直觉信号: "泛流量型" 笔记互动数据最差,但评论区出现的是最专业的用户对话 (参数 / 楼板 / 邻居)。说明这种形态吸引的是"质优"用户而非"量大"用户,只是被低估了。',
|
|
|
+ how: [
|
|
|
+ '把"泛流量相关型"重命名为"专业体验交流型"',
|
|
|
+ '下一轮投入比例从 12% 提到 30%',
|
|
|
+ '简报偏向引导用户参数交流 / 真实使用场景',
|
|
|
+ '考核指标从"互动率"切到"评论质量分" (专业评论占比)',
|
|
|
+ ],
|
|
|
+ outcome: '高质量评论占比 +50%,品牌专业人群心智渗透 ×2,长期复购率 +15%',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'B4', cat: 'B · 内容形态', p: 'P1', t: '真实邻居访谈视频',
|
|
|
+ signal: '沐阳云南: 跑步机声 + 邻居反馈"没听到"',
|
|
|
+ action: '找 5 位老用户上门,让楼下邻居出镜确认,这是 KOL 软文做不到的真实信任',
|
|
|
+ why: 'KOL 说"很安静"用户半信半疑,KOL 楼下邻居说"没听到"用户立刻信任。第三方背书是降低决策门槛最锋利的内容形式。',
|
|
|
+ how: [
|
|
|
+ '招募 5 位老用户参与"邻居访谈"项目',
|
|
|
+ '上门拍摄: 跑步机使用 + 楼下邻居出镜反馈',
|
|
|
+ '剪成 1 分钟短视频,小红书 + 视频号同步',
|
|
|
+ '门店关键位置二维码引流',
|
|
|
+ ],
|
|
|
+ outcome: '"扰邻焦虑"心理门槛进一步降低,商品房用户决策周期 -30%',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'C1', cat: 'C · 人群渠道', p: 'P0', t: 'KOC 性别/年龄结构调整',
|
|
|
+ signal: '本期 43 位 KOC 全为萌系女性昵称,跑步机决策人群错位',
|
|
|
+ action: '男性 30% + 宝爸宝妈 20% + 30+ 中产女性 10%,等于换一批用户',
|
|
|
+ why: '跑步机决策人 ≠ 萌系女性。家庭购买决策更多由男性 / 宝爸宝妈 / 30+ 中产驱动。当前 KOC 全为单身年轻女性,看起来"可爱"但不是真实购买人群,转化率天然低。',
|
|
|
+ how: [
|
|
|
+ 'KOC 选号比例: 男性 30% + 宝爸宝妈 20% + 30+ 中产女性 10% + 年轻女性 40%',
|
|
|
+ '内容形态适配: 男性偏参数评测 / 宝爸宝妈偏家庭场景 / 中产偏品质生活',
|
|
|
+ '画像审核: 选号时验证粉丝主体年龄性别,避免"号是中产但粉丝是大学生"',
|
|
|
+ '建立 KOC 多维度黑灰白名单',
|
|
|
+ ],
|
|
|
+ outcome: '决策人群错位被纠正,到店/到电商转化率 +30-50%',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'C2', cat: 'C · 人群渠道', p: 'P1', t: '商品房新业主精准包',
|
|
|
+ signal: '沐阳: "商品房 / 开发商问题"',
|
|
|
+ action: '定向 3 年内交付的高层公寓用户,这是扰邻焦虑最强的目标人群',
|
|
|
+ why: '商品房新业主 = 刚装修完 + 有居家运动需求 + 最在意扰邻 = 跑步机最优潜在客户群。目前没有任何渠道精准触达,纯靠泛流量碰运气。',
|
|
|
+ how: [
|
|
|
+ '与房产 KOC / 装修 KOC 合作,精准触达交付 3 年内业主',
|
|
|
+ '与房地产物业合作: 业主群发"扰邻友好家电推荐"',
|
|
|
+ '小红书"装修笔记"达人投放,植入跑步机',
|
|
|
+ '高层公寓集中区域线下广告 (电梯框架 / 社区入口)',
|
|
|
+ ],
|
|
|
+ outcome: '高意向商品房新业主触达 +1000 起步,客单价高于均值',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'C3', cat: 'C · 人群渠道', p: 'P1', t: '减肥搭子社交营 (噪声资产化)',
|
|
|
+ signal: '47% 互助互赞噪声实为真实减肥群体寻找搭子',
|
|
|
+ action: '#舒华 30 天减肥搭子营 官方话题 + 企微私域 + 月度 PK 赛事,把噪声变长效资产',
|
|
|
+ why: '本期评论里 47% 互赞互助 "噪声",其实是真实的减肥群体在寻找运动搭子。把"噪声"变成"社群资产" = 把流量变成用户。',
|
|
|
+ how: [
|
|
|
+ '推 #舒华 30 天减肥搭子营 官方话题 (公司账号背书)',
|
|
|
+ '小红书群 + 企微私域承接报名',
|
|
|
+ '月度 PK 赛事 + 实物奖励 (每月轻奢 SKU)',
|
|
|
+ '完赛用户给优惠 / 老带新返利',
|
|
|
+ ],
|
|
|
+ outcome: '私域用户 +5000 起步,长期复购率 ×2,内容生态自循环',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'C4', cat: 'C · 人群渠道', p: 'P0', t: '一线 + 新一线 IP 占比 ≥ 50%',
|
|
|
+ signal: '本期评论 IP 缺京沪深广杭蓉',
|
|
|
+ action: '跑步机是大件家居,一线城市才有支付力 + 居住空间,选号必须明确 IP 配比',
|
|
|
+ why: '跑步机客单价 2k+ 是大件家居,需要支付力 + 居住空间。一线/新一线人群是核心。本期 IP 严重缺一线,投放打到了价格敏感人群。',
|
|
|
+ how: [
|
|
|
+ 'KOC 选号硬性规定: 一线 + 新一线 IP 占比 ≥ 50% (北上广深 + 杭州/成都/武汉)',
|
|
|
+ 'IP 验证作为蒲公英结案核心指标',
|
|
|
+ '一线城市同步投放线下广告 (社区/健身房/物业)',
|
|
|
+ '新一线节点城市做品牌活动',
|
|
|
+ ],
|
|
|
+ outcome: '客单价 +15-20%,触达决策人群更精准,品牌势能提升',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'D1', cat: 'D · 投放机制', p: 'P0', t: '类目标签从家居/好物迁到健身/减脂',
|
|
|
+ signal: '本期挂"家居 22 + 好物 21",但搜索词是"跑步机推荐/家用跑步机"',
|
|
|
+ action: '简报锁定 SEO 标签 居家健身 / 减脂塑形 / 跑步机推荐 / 健身房替代',
|
|
|
+ why: '"家居/好物"标签竞争激烈且与"跑步机"心智弱关联;"健身/减脂/居家健身"才是用户真实搜索路径。换标签 = 换流量池,免费降本。',
|
|
|
+ how: [
|
|
|
+ '简报硬性要求: 标签必含"居家健身/减脂塑形/跑步机推荐/健身房替代"',
|
|
|
+ '禁用"家居好物"等泛标签',
|
|
|
+ '关键词扩展: "家用跑步机选购" / "小户型跑步机" 等长尾词',
|
|
|
+ '考核 SEO 流量入口占比 ≥ 30%',
|
|
|
+ ],
|
|
|
+ outcome: 'SEO 流量入口 +50-100%,搜索流量获客成本下降 30%',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'D2', cat: 'D · 投放机制', p: 'P1', t: 'MCN 来源审计 + 多源选号',
|
|
|
+ signal: '粉丝量 1k-2k 高度同质,疑似单一 MCN 包号',
|
|
|
+ action: '≥ 3 家 MCN + ≥ 20% 蒲公英直选 + ≥ 10% 自建池 + 粉丝阶梯化金字塔结构',
|
|
|
+ why: '本期 KOC 粉丝量过度集中 1k-2k,疑似单一 MCN 提供"扁平池",这种号有刷数据嫌疑且互动质量低。多 MCN + 直选打散来源,保证内容生态多样性。',
|
|
|
+ how: [
|
|
|
+ '≥ 3 家不同 MCN 提供候选号',
|
|
|
+ '≥ 20% 蒲公英直选 (跳过 MCN)',
|
|
|
+ '≥ 10% 自建 KOC 池 (品牌培育)',
|
|
|
+ '粉丝量阶梯: 头部 5% / 腰部 30% / 尾部 65%',
|
|
|
+ ],
|
|
|
+ outcome: '内容生态多样性提升,刷量风险下降,长尾流量稳定',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'D3', cat: 'D · 投放机制', p: 'P2', t: '7 天上门试驾 O2O',
|
|
|
+ signal: '星星不懂星星山东: "免费体验欢迎来锻炼"',
|
|
|
+ action: '借鉴戴森/特斯拉,跑步机 7 天上门 + 不满意退款,降低决策门槛,KOC 矩阵推广',
|
|
|
+ why: '跑步机决策成本高 (大件 + 占地 + 长期使用),7 天试驾 = 把决策门槛拆到地板。戴森吸尘器 / 特斯拉试驾都是验证过的成熟模型。',
|
|
|
+ how: [
|
|
|
+ '7 天上门试驾, 不满意 100% 退款',
|
|
|
+ 'KOC 矩阵推广"试用日记"内容',
|
|
|
+ '配套售后 SOP: 退货免费上门取',
|
|
|
+ '小红书 / 抖音"7 天试驾打卡"话题运营',
|
|
|
+ ],
|
|
|
+ outcome: '决策门槛大幅降低,转化率 ×1.5-2,试驾用户复购率 +50%',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'E1', cat: 'E · 数据机制', p: 'P0', t: '投后 24h 抓取 + 博主行为预警',
|
|
|
+ signal: 'xlsx 19 评论 → API 17, 缺失 2 条 (可能博主自删)',
|
|
|
+ action: '24h 内首版数据快照,捕捉删评/删笔记信号,识别评论区翻车风险',
|
|
|
+ why: '本次复盘发现 xlsx 报备 19 条评论 → API 实际抓到 17 条,差额 2 条可能是博主自删。这是评论区出现负面/翻车的强信号,但我们目前无法及时发现。',
|
|
|
+ how: [
|
|
|
+ '采集脚本固化为 24h 自动定时任务',
|
|
|
+ '建立"博主自删评论"预警 (报备 vs 实抓差额 ≥ 3 即告警)',
|
|
|
+ '对应 KOC 客服跟进核实是否有翻车',
|
|
|
+ '建立长期 KOC 风险数据库',
|
|
|
+ ],
|
|
|
+ outcome: '评论区翻车从"事后才知道"变成"6h 内介入",品牌信任度兜底',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'E2', cat: 'E · 数据机制', p: 'P1', t: '3 天数据保护期 + 死笔记自动补流',
|
|
|
+ signal: '11/43 篇互动 < 5,33/43 篇产出极差 = ¥7000+ 浪费',
|
|
|
+ action: '投后 3 天数据 < 阈值的笔记,自动触发 ¥30-50 薯条二次投流补救,而非直接放弃',
|
|
|
+ why: '本期 11/43 篇互动 < 5,长尾零产出占 26%,这部分预算几乎是沉没成本。机制化止损 + 二次投流 = 把死笔记救回来。',
|
|
|
+ how: [
|
|
|
+ '投后 72h 自动检查互动数据',
|
|
|
+ '互动 < 5: 自动追加 ¥30-50 薯条二次抢救',
|
|
|
+ '抢救后仍 < 10: 标记 KOC 为低效 (3 次失败拉黑)',
|
|
|
+ '建立 KOC 历史投放表现数据库',
|
|
|
+ ],
|
|
|
+ outcome: '长尾零互动篇数下降 30-50%,投放 ROI 持续优化',
|
|
|
+ },
|
|
|
];
|
|
|
|
|
|
const TOP5 = ['A1', 'A2', 'C1', 'B3', 'E1'];
|
|
|
@@ -244,9 +510,9 @@ const topicRows = TOPICS.map((t) => {
|
|
|
</tr>`;
|
|
|
}).join('');
|
|
|
|
|
|
-const realCommentList = realComments.sort((a, b) => b.like - a.like).slice(0, 12).map((c) => `
|
|
|
+const realCommentList = topPicks.map(({ topic, c }) => `
|
|
|
<div class="quote">
|
|
|
- <div class="quote-meta"><span class="tag">${esc(c.koc)}</span> <span class="text-xs text-3">${esc(c.type || '')} · ${esc(c.ip || '')} · 赞 ${c.like}</span></div>
|
|
|
+ <div class="quote-meta"><span class="tag green">${esc(topic)}</span> <span class="tag gray">${esc(c.koc)}</span> <span class="text-xs text-3">${esc(c.ip || '')}</span></div>
|
|
|
<div class="quote-text">${esc(c.content.replace(/\n/g, ' '))}</div>
|
|
|
</div>`).join('');
|
|
|
|
|
|
@@ -296,7 +562,7 @@ section::before { content: attr(data-num); position: absolute; top: 32px; right:
|
|
|
.grid { display: grid; gap: 22px; }
|
|
|
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); }
|
|
|
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
|
|
|
-.grid-4 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
|
|
|
+.grid-4 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
|
|
|
|
|
|
.card { background: linear-gradient(180deg, rgba(255,255,255,0.025) 0%, rgba(255,255,255,0.005) 100%), var(--bg-1); border: 1px solid var(--border); border-radius: 12px; padding: 28px; transition: transform 0.3s, border-color 0.3s; }
|
|
|
.card:hover { transform: translateY(-3px); border-color: var(--border-strong); }
|
|
|
@@ -305,10 +571,10 @@ section::before { content: attr(data-num); position: absolute; top: 32px; right:
|
|
|
.card-rose { border-left: 3px solid var(--rose); }
|
|
|
.card-blue { border-left: 3px solid var(--blue); }
|
|
|
|
|
|
-.metric { background: var(--bg-1); border: 1px solid var(--border); border-radius: 12px; padding: 28px; position: relative; overflow: hidden; }
|
|
|
+.metric { background: var(--bg-1); border: 1px solid var(--border); border-radius: 12px; padding: 24px; position: relative; overflow: hidden; min-width: 0; }
|
|
|
.metric::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; background: linear-gradient(90deg, var(--amber), transparent); opacity: 0.6; }
|
|
|
.metric-label { font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.15em; text-transform: uppercase; color: var(--text-3); margin-bottom: 10px; }
|
|
|
-.metric-value { font-family: var(--font-head); font-weight: 800; font-size: clamp(2rem, 3.6vw, 3rem); letter-spacing: -0.03em; line-height: 1; }
|
|
|
+.metric-value { font-family: var(--font-head); font-weight: 800; font-size: clamp(1.6rem, 2.6vw, 2.3rem); letter-spacing: -0.03em; line-height: 1.05; word-break: break-all; }
|
|
|
.metric-unit { font-family: var(--font-mono); font-size: 0.85rem; color: var(--amber); font-weight: 500; margin-left: 4px; }
|
|
|
.metric-sub { margin-top: 14px; font-size: 0.82rem; color: var(--text-2); line-height: 1.5; }
|
|
|
|
|
|
@@ -456,8 +722,8 @@ td.sample { color: var(--text-2); font-size: 0.82rem; max-width: 380px; }
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <h3 class="h2">真实评论原声 (Top 12 by 点赞)</h3>
|
|
|
- <p class="sub">已剔除互赞互助噪声,展示具有产品对话价值的真实评论。</p>
|
|
|
+ <h3 class="h2">真实评论原声 (Top ${topPicks.length})</h3>
|
|
|
+ <p class="sub">产品强相关 · 多主题轮询 · 已剔除噪声/托式营销/博主自评,展示具有产品对话价值的真实评论。</p>
|
|
|
<div class="grid grid-2">${realCommentList}</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
@@ -562,41 +828,89 @@ td.sample { color: var(--text-2); font-size: 0.82rem; max-width: 380px; }
|
|
|
}).join('')}
|
|
|
</div>
|
|
|
|
|
|
- <h3 class="h2">完整 ${OPPORTUNITIES.length} 个新机会矩阵</h3>
|
|
|
- <p class="sub">按 5 大象限分类: <span class="tag" style="margin-right:8px">A 产品卖点</span><span class="tag green" style="margin-right:8px">B 内容形态</span><span class="tag purple" style="margin-right:8px">C 人群渠道</span><span class="tag blue" style="margin-right:8px">D 投放机制</span><span class="tag rose">E 数据机制</span></p>
|
|
|
-
|
|
|
- <div class="grid grid-2">
|
|
|
- ${['A · 产品卖点','B · 内容形态','C · 人群渠道','D · 投放机制','E · 数据机制'].map((cat) => {
|
|
|
- const list = OPPORTUNITIES.filter((o) => o.cat === cat);
|
|
|
- const colorClass = cat.startsWith('A') ? 'card-accent' : cat.startsWith('B') ? 'card-green' : cat.startsWith('C') ? '' : cat.startsWith('D') ? 'card-blue' : 'card-rose';
|
|
|
- const tagClass = cat.startsWith('A') ? '' : cat.startsWith('B') ? 'green' : cat.startsWith('C') ? 'purple' : cat.startsWith('D') ? 'blue' : 'rose';
|
|
|
- return `<div class="card ${colorClass}">
|
|
|
- <h3 class="h2" style="margin-top:0;margin-bottom:18px">${esc(cat)}</h3>
|
|
|
- ${list.map((o) => `
|
|
|
- <div style="padding:14px 0;border-bottom:1px solid var(--border)">
|
|
|
- <div style="display:flex;gap:8px;align-items:center;margin-bottom:6px;flex-wrap:wrap">
|
|
|
- <span class="tag ${tagClass}" style="font-family:var(--font-mono)">${o.id}</span>
|
|
|
- <span class="tag ${o.p === 'P0' ? 'rose' : o.p === 'P1' ? '' : 'purple'}">${o.p}</span>
|
|
|
- <strong style="font-size:0.96rem">${esc(o.t)}</strong>
|
|
|
- </div>
|
|
|
- <p style="color:var(--text-3);font-size:0.74rem;margin-bottom:4px">📍 ${esc(o.signal)}</p>
|
|
|
- <p style="color:var(--text-2);font-size:0.84rem;line-height:1.6">→ ${esc(o.action)}</p>
|
|
|
- </div>
|
|
|
- `).join('')}
|
|
|
- </div>`;
|
|
|
- }).join('')}
|
|
|
+ <p class="sub" style="margin-top:32px">完整 ${OPPORTUNITIES.length} 个新机会按 5 大象限分独立章节深度展开,见下方 Section 06.A ~ 06.E。</p>
|
|
|
+ <div class="toc" style="margin-top:24px">
|
|
|
+ <a href="#sec-6a" style="border-color:var(--amber);color:var(--amber)">06.A 产品卖点 (${OPPORTUNITIES.filter((o)=>o.cat.startsWith('A')).length})</a>
|
|
|
+ <a href="#sec-6b" style="border-color:var(--green);color:var(--green)">06.B 内容形态 (${OPPORTUNITIES.filter((o)=>o.cat.startsWith('B')).length})</a>
|
|
|
+ <a href="#sec-6c" style="border-color:var(--purple);color:var(--purple)">06.C 人群渠道 (${OPPORTUNITIES.filter((o)=>o.cat.startsWith('C')).length})</a>
|
|
|
+ <a href="#sec-6d" style="border-color:var(--blue);color:var(--blue)">06.D 投放机制 (${OPPORTUNITIES.filter((o)=>o.cat.startsWith('D')).length})</a>
|
|
|
+ <a href="#sec-6e" style="border-color:var(--rose);color:var(--rose)">06.E 数据机制 (${OPPORTUNITIES.filter((o)=>o.cat.startsWith('E')).length})</a>
|
|
|
</div>
|
|
|
|
|
|
- <h3 class="h2">方法论 — 4 条挖掘原则</h3>
|
|
|
+ <h3 class="h2" style="margin-top:48px">方法论 — 4 条挖掘原则</h3>
|
|
|
<div class="grid grid-4">
|
|
|
- <div class="card"><div class="tag">原则 1</div><h4 style="margin:10px 0">小样本不影响信号方向</h4><p style="color:var(--text-2);font-size:0.84rem">1 条专业参数评论 = 1 个产品价值锚点,**信号密度** > 统计显著性</p></div>
|
|
|
- <div class="card"><div class="tag green">原则 2</div><h4 style="margin:10px 0">每条评论都是潜在购买者</h4><p style="color:var(--text-2);font-size:0.84rem">不是数据,是真人,值得**逐字解读** — 词序、表情、IP 全有意义</p></div>
|
|
|
+ <div class="card"><div class="tag">原则 1</div><h4 style="margin:10px 0">小样本不影响信号方向</h4><p style="color:var(--text-2);font-size:0.84rem">1 条专业参数评论 = 1 个产品价值锚点,<strong>信号密度</strong> > 统计显著性</p></div>
|
|
|
+ <div class="card"><div class="tag green">原则 2</div><h4 style="margin:10px 0">每条评论都是潜在购买者</h4><p style="color:var(--text-2);font-size:0.84rem">不是数据,是真人,值得<strong>逐字解读</strong> — 词序、表情、IP 全有意义</p></div>
|
|
|
<div class="card"><div class="tag purple">原则 3</div><h4 style="margin:10px 0">反直觉信号 > 平均值</h4><p style="color:var(--text-2);font-size:0.84rem">"泛流量型"互动最低却产生最高质量对话 — 这就是被低估的机会</p></div>
|
|
|
- <div class="card"><div class="tag rose">原则 4</div><h4 style="margin:10px 0">缺席与出现同等重要</h4><p style="color:var(--text-2);font-size:0.84rem">一线 IP 缺席、男性 KOC 缺席、品牌词命中 0 — **"没有"才是最大的机会**</p></div>
|
|
|
+ <div class="card"><div class="tag rose">原则 4</div><h4 style="margin:10px 0">缺席与出现同等重要</h4><p style="color:var(--text-2);font-size:0.84rem">一线 IP 缺席、男性 KOC 缺席、品牌词命中 0 — <strong>"没有"才是最大的机会</strong></p></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
+${(() => {
|
|
|
+ const QUADRANTS = [
|
|
|
+ { letter: 'A', cat: 'A · 产品卖点', color: 'amber', tagClass: '', accent: 'card-accent', desc: '从用户评论里反推的产品/卖点级机会 — 把"用户已经说出口"的需求变成可执行的内容指引/SKU/卖点。' },
|
|
|
+ { letter: 'B', cat: 'B · 内容形态', color: 'green', tagClass: 'green', accent: 'card-green', desc: '内容打法升级 — 同样的预算,如何把"被低估的内容形态"翻转成主战场。' },
|
|
|
+ { letter: 'C', cat: 'C · 人群渠道', color: 'purple', tagClass: 'purple', accent: '', desc: '人群和渠道的二八优化 — 让每一分预算都投在"会真买跑步机"的人身上,而不是看了不下单的远方观众。' },
|
|
|
+ { letter: 'D', cat: 'D · 投放机制', color: 'blue', tagClass: 'blue', accent: 'card-blue', desc: '投放过程的机制化 — 把"凭感觉发笔记"变成"算法驱动止损/加投/预警",每轮都比上轮更聪明。' },
|
|
|
+ { letter: 'E', cat: 'E · 数据机制', color: 'rose', tagClass: 'rose', accent: 'card-rose', desc: '长效数据资产 — 投后快照、博主行为预警,这是可以持续复用 N 轮投放的底层基建。' },
|
|
|
+ ];
|
|
|
+ return QUADRANTS.map((q) => {
|
|
|
+ const list = OPPORTUNITIES.filter((o) => o.cat === q.cat);
|
|
|
+ return `
|
|
|
+<!-- ============= 6.${q.letter} ${q.cat} ============= -->
|
|
|
+<section id="sec-6${q.letter.toLowerCase()}" data-num="06.${q.letter} / ${q.cat.split('·')[1].trim()}" style="justify-content:flex-start">
|
|
|
+ <div class="inner">
|
|
|
+ <div class="eyebrow" style="color:var(--${q.color})">Section 06.${q.letter} · ${q.cat}</div>
|
|
|
+ <h2 class="h1" style="color:var(--${q.color})">${esc(q.cat)} <span style="color:var(--text-3);font-size:0.5em;font-weight:500">${list.length} 个新机会</span></h2>
|
|
|
+ <p class="sub">${esc(q.desc)}</p>
|
|
|
+
|
|
|
+ <div class="grid" style="gap:28px;margin-top:32px">
|
|
|
+ ${list.map((o) => `
|
|
|
+ <div class="card ${q.accent}" style="position:relative">
|
|
|
+ <div style="position:absolute;top:18px;right:24px;font-family:var(--font-mono);font-size:2.4rem;font-weight:800;color:var(--${q.color});opacity:0.15;line-height:1">${o.id}</div>
|
|
|
+ <div style="display:flex;gap:10px;align-items:center;margin-bottom:14px;flex-wrap:wrap">
|
|
|
+ <span class="tag ${q.tagClass}" style="font-family:var(--font-mono);font-size:0.8rem">${o.id}</span>
|
|
|
+ <span class="tag ${o.p === 'P0' ? 'rose' : o.p === 'P1' ? '' : 'purple'}">${o.p}</span>
|
|
|
+ <h3 style="font-family:var(--font-head);font-size:1.4rem;font-weight:700;letter-spacing:-0.01em">${esc(o.t)}</h3>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="grid grid-3" style="gap:18px;margin-top:18px">
|
|
|
+ <div style="background:var(--bg-0);border-radius:8px;padding:18px;border-left:2px solid var(--${q.color})">
|
|
|
+ <div class="metric-label" style="margin-bottom:8px">📍 信号 / Signal</div>
|
|
|
+ <p style="color:var(--text-2);font-size:0.88rem;line-height:1.65">${esc(o.signal)}</p>
|
|
|
+ </div>
|
|
|
+ <div style="background:var(--bg-0);border-radius:8px;padding:18px;border-left:2px solid var(--${q.color})">
|
|
|
+ <div class="metric-label" style="margin-bottom:8px">🎯 行动 / Action</div>
|
|
|
+ <p style="color:var(--text-1);font-size:0.88rem;line-height:1.65;font-weight:500">${esc(o.action)}</p>
|
|
|
+ </div>
|
|
|
+ <div style="background:var(--bg-0);border-radius:8px;padding:18px;border-left:2px solid var(--${q.color})">
|
|
|
+ <div class="metric-label" style="margin-bottom:8px">📈 预期 / Outcome</div>
|
|
|
+ <p style="color:var(--text-2);font-size:0.88rem;line-height:1.65">${esc(o.outcome || '-')}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style="margin-top:22px;padding:20px;background:var(--bg-0);border-radius:8px">
|
|
|
+ <div class="metric-label" style="margin-bottom:10px">💡 为什么要做 / Why</div>
|
|
|
+ <p style="color:var(--text-2);font-size:0.92rem;line-height:1.75">${esc(o.why || '-')}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ ${Array.isArray(o.how) && o.how.length ? `
|
|
|
+ <div style="margin-top:18px;padding:20px;background:var(--bg-0);border-radius:8px">
|
|
|
+ <div class="metric-label" style="margin-bottom:12px">🛠️ 如何执行 / How (${o.how.length} 步)</div>
|
|
|
+ <ol style="margin:0;padding-left:24px;color:var(--text-2);font-size:0.9rem;line-height:1.85">
|
|
|
+ ${o.how.map((step) => `<li style="margin-bottom:6px">${esc(step)}</li>`).join('')}
|
|
|
+ </ol>
|
|
|
+ </div>
|
|
|
+ ` : ''}
|
|
|
+ </div>
|
|
|
+ `).join('')}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</section>`;
|
|
|
+ }).join('');
|
|
|
+})()}
|
|
|
+
|
|
|
<!-- ============= 页脚 ============= -->
|
|
|
<section style="min-height:auto;padding:60px 24px;background:var(--bg-1)" data-num="END">
|
|
|
<div class="inner" style="text-align:center">
|
|
|
@@ -771,10 +1085,10 @@ for (const t of TOPICS) {
|
|
|
}
|
|
|
M('');
|
|
|
|
|
|
-M('### 真实评论原声 Top 12 (剔除噪声)');
|
|
|
+M(`### 真实评论原声 Top ${topPicks.length} (产品强相关 · 多主题轮询)`);
|
|
|
M('');
|
|
|
-for (const c of realComments.sort((a,b)=>b.like-a.like).slice(0,12)) {
|
|
|
- M(`- **[${c.koc}/${c.type||'-'}]** \`${c.nick||'-'}@${c.ip||'-'}\` *(赞 ${c.like})*: ${c.content.replace(/\n/g,' ')}`);
|
|
|
+for (const { topic, c } of topPicks) {
|
|
|
+ M(`- **[${topic}]** \`${c.koc}@${c.ip||'-'}\`: ${c.content.replace(/\n/g,' ')}`);
|
|
|
}
|
|
|
M('');
|
|
|
M('---');
|