| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871 |
- #!/usr/bin/env node
- const fs = require('fs');
- const path = require('path');
- const ROOT = path.resolve(__dirname, '..', '..', '..');
- const OUT_FILE = path.join(ROOT, 'reports', 'ks-lanlingqiuzhi', 'ks-voc-insight-report-v7.html');
- const COVER_STATS = [
- { num: '7,902', lbl: 'VOC总量' },
- { num: '380', lbl: '视频数' },
- { num: '19', lbl: '关键词数' },
- { num: '9,455万', lbl: '总播放量' },
- ];
- const KPIS = [
- { label: 'VOC总量', value: '7,902', sub: '高质量评论', color: 'var(--amber)' },
- { label: '视频覆盖', value: '380', sub: '条视频内容', color: 'var(--green)' },
- { label: '情绪方向', value: '负向主导', sub: '风险焦虑最强', color: 'var(--rose)' },
- { label: '核心关键词', value: '骗/坑/黑中介', sub: '热度最高', color: 'var(--amber)' },
- ];
- const KUAIKUAI_DATA = [
- { label: '月度服务求职者', value: '超2.5亿人次', note: '截至2024年底' },
- { label: '平台蓝领用户规模', value: '约2.5亿人', note: '2024年快聘峰会' },
- { label: '全年直播带岗场次', value: '超500万场', note: '2024年全年' },
- { label: '日收简历峰值', value: '突破5万份', note: '2025年2月' },
- { label: '带动就业机会', value: '4,320万个', note: '截至2024年底' },
- ];
- const COMPETITORS = [
- { name: '快手快聘', type: '直播带岗', users: '2.5亿', trend: '快速增长', color: 'var(--green)', strength: '用户渗透率极高', weak: '信息审核薄弱', barColor: '#00DC82' },
- { name: 'BOSS直聘', type: '算法匹配', users: '约3000万', trend: '稳步增长', color: 'var(--blue)', strength: '匹配准确率高', weak: '情绪获得感弱', barColor: '#3B82F6' },
- { name: '58同城', type: '分类信息', users: '规模下滑', trend: '持续萎缩', color: 'var(--text-3)', strength: '传统市场份额', weak: '虚假信息泛滥', barColor: '#6B6B6B' },
- ];
- const RADAR_DATA = [
- { label: '信息真实度', ks: 3, boss: 4, wu: 2 },
- { label: '薪资透明度', ks: 4, boss: 4, wu: 2 },
- { label: '权益保障', ks: 2, boss: 3, wu: 2 },
- { label: '匹配准确率', ks: 3, boss: 5, wu: 3 },
- { label: '求职低门槛', ks: 5, boss: 4, wu: 3 },
- { label: '沟通即时性', ks: 5, boss: 4, wu: 1 },
- { label: '信任建立', ks: 4, boss: 3, wu: 1 },
- { label: '职业发展', ks: 2, boss: 3, wu: 1 },
- { label: '平台审核', ks: 2, boss: 4, wu: 3 },
- { label: '情绪获得感', ks: 5, boss: 2, wu: 0 },
- ];
- const RISK_SIGNALS = [
- { label: '骗', value: 252, heat: 514788, color: 'var(--rose)' },
- { label: '黑中介', value: 109, heat: 393762, color: 'var(--rose)' },
- { label: '坑', value: 92, heat: 375264, color: 'var(--rose)' },
- { label: '套路', value: 45, heat: 144842, color: 'var(--orange)' },
- { label: '黑厂', value: 43, heat: 123531, color: 'var(--rose)' },
- { label: '押金', value: 105, heat: 95543, color: 'var(--amber)' },
- { label: '不退', value: 70, heat: 84106, color: 'var(--amber)' },
- { label: '陷阱', value: 21, heat: 74090, color: 'var(--orange)' },
- ];
- const CARE_FACTORS = [
- { label: '包吃包住', value: 31, heat: 89190, color: 'var(--green)' },
- { label: '工资', value: 186, heat: 89081, color: 'var(--green)' },
- { label: '加班费', value: 20, heat: 43986, color: 'var(--blue)' },
- { label: '吃住', value: 21, heat: 34060, color: 'var(--green)' },
- { label: '住宿', value: 15, heat: 25224, color: 'var(--blue)' },
- { label: '当天入职', value: 7, heat: 19264, color: 'var(--purple)' },
- { label: '日结', value: 30, heat: 12651, color: 'var(--purple)' },
- { label: '发工资', value: 29, heat: 4773, color: 'var(--amber)' },
- ];
- const CARE_SIGNALS = [
- { label: '信息真实不被骗', pct: 85, color: 'var(--rose)' },
- { label: '工资按时发放', pct: 72, color: 'var(--green)' },
- { label: '包吃包住', pct: 65, color: 'var(--green)' },
- { label: '当天入职', pct: 55, color: 'var(--purple)' },
- { label: '眼见为实', pct: 48, color: 'var(--blue)' },
- ];
- const HIGH_LIKED = [
- { rank: 1, likes: 6276, expr: '22年差点被骗去当咯咯哒 说什么模特走台下面的人送花就有提成', kw: '入职被坑', hot: true },
- { rank: 2, likes: 2209, expr: '去买几个鸡蛋,打碎,放在沙发下面...', kw: '押金不退', hot: true },
- { rank: 3, likes: 808, expr: '我在广西就遇到过房东死活不退押金...', kw: '押金不退', hot: false },
- { rank: 4, likes: 533, expr: '做的不错,就该那样!有些房东太不像话了!押金不退,还有理由了!', kw: '押金不退', hot: false },
- { rank: 5, likes: 491, expr: '无论怎么样,反正黑中介是赚了', kw: '黑中介', hot: false },
- { rank: 6, likes: 422, expr: '那暑假工能做什么啊', kw: '入职被坑', hot: false },
- { rank: 7, likes: 403, expr: '干的漂亮,支持小伙子不应该判刑...', kw: '黑中介', hot: false },
- { rank: 8, likes: 312, expr: '都绑起来了,都投降了,那人还要去喷那个喷雾', kw: '黑中介', hot: false },
- { rank: 9, likes: 311, expr: '为什么南方的房东都不退押金', kw: '押金不退', hot: false },
- { rank: 10, likes: 228, expr: '只要收身份证的都是,正常的面试只需要填表就行', kw: '黑中介', hot: false },
- ];
- const SALARY_VOC = [
- { likes: 38195, expr: '不给加班费就不加班合理吗?#记录餐饮人生活每一天 #餐饮创业 #现场实拍', kw: '加班费' },
- { likes: 21817, expr: '#打工妹 #工资条 #打工妹的日常生活 #电子厂', kw: '发工资' },
- { likes: 51, expr: '确实是像你这样说的,跑外卖只要多劳多得,工资不会少发。我坚持了两个月,第3个月的时候我已经摆烂了', kw: '工资靠谱' },
- ];
- const FOOD_VOC = [
- { likes: 84, expr: '我年前就是在洗碗又累又脏,基本上10个小时没得休息,只有吃销10多分钟,我都不想干了', kw: '包吃包住' },
- { likes: 17397, expr: '#不体检不收费当天安排 #当天来当天入职安排住宿 #岗位轻松干活不累想工作就来 #包吃包住当天安排住宿', kw: '当天入职' },
- { likes: 15768, expr: '距你0.9km #找工作 #包吃包住 #勤杂工 #大龄工', kw: '包吃包住' },
- ];
- const HYPOTHESES = [
- { h: 'H1', title: '快聘是蓝领求职主阵地', count: 596, color: '#F5A623' },
- { h: 'H2', title: '直播带岗降低信息不对称', count: 1930, color: '#00DC82' },
- { h: 'H3', title: '薪资透明度是核心关切', count: 1842, color: '#8B5CF6' },
- { h: 'H4', title: '食宿条件影响求职决策', count: 2194, color: '#FF4D8D' },
- { h: 'H5', title: '入职流程便捷性', count: 595, color: '#3B82F6' },
- { h: 'H6', title: '求职被骗是普遍焦虑', count: 4435, color: '#EF4444' },
- { h: 'H7', title: '黑中介是核心痛点', count: 3705, color: '#F97316' },
- { h: 'H8', title: '工资结算方式差异', count: 2417, color: '#6B6B6B' },
- ];
- const EMOTION_KEYWORDS = [
- { word: '"眼见为实"', desc: '消解信息不对称带来的不安全感', emoji: '👁️', color: 'var(--amber)' },
- { word: '"老铁推荐"', desc: '以熟人身份降低求职者心理防备', emoji: '🤝', color: 'var(--green)' },
- { word: '"来了就是兄弟"', desc: '强调归属感和人情味', emoji: '🏠', color: 'var(--purple)' },
- { word: '"有尊严地赚钱"', desc: '回应蓝领的身份焦虑与社会认同诉求', emoji: '💪', color: 'var(--blue)' },
- { word: '"不骗人、不套路"', desc: '针对虚假宣传痛点', emoji: '🛡️', color: 'var(--rose)' },
- { word: '"独门绝技"', desc: '放大蓝领专业技能价值', emoji: '⚡', color: 'var(--orange)' },
- ];
- const STRATEGY_RECS = [
- { num: '1', title: '建立分级审核体系', desc: '对招聘主播实行分级管理,对粉丝量级大的头部主播实行更审慎的审核标准', color: 'var(--amber)' },
- { num: '2', title: '强化"信任基建"投入', desc: '将信息审核能力作为快聘业务的核心基建来建设,建立招聘方信用评级和黑名单机制', color: 'var(--green)' },
- { num: '3', title: '深化企业直招占比', desc: '减少对中介端的依赖,提高B端企业直招占比,从根本上降低信息失真风险', color: 'var(--blue)' },
- { num: '4', title: '放大"眼见为实"差异化优势', desc: '继续强化直播展示真实工作环境的核心卖点,将车间、宿舍、食堂直播打造为标准化招聘流程', color: 'var(--purple)' },
- { num: '5', title: '差异化提升情绪价值', desc: '利用"老铁信任"和"眼见为实"的天然优势,将情绪价值从"内容传播手段"升级为"用户留存壁垒"', color: 'var(--orange)' },
- ];
- const RISKS = [
- { type: '信任透支', desc: '虚假招聘信息泛滥,透支"老铁信任"', prob: '高', impact: '高', strategy: '审核体系升级+信用评级', color: 'var(--rose)' },
- { type: '监管压力', desc: '五部门行政指导要求强化审核', prob: '高', impact: '高', strategy: '主动对标"五个到位"', color: 'var(--rose)' },
- { type: '竞争跟进', desc: 'BOSS直聘加码蓝领,58同城转型', prob: '中', impact: '中', strategy: '差异化壁垒+效率提升', color: 'var(--amber)' },
- { type: '匹配效率低', desc: '流量大但精准度不如算法平台', prob: '中', impact: '中', strategy: '引入精准匹配算法', color: 'var(--amber)' },
- ];
- const FOUR_P = {
- product: {
- title: '产品策略:直播带岗 × 信任基建',
- points: ['核心产品:快手快聘直播带岗服务', '差异化卖点:眼见为实 / 老铁信任 / 零门槛求职', '产品升级方向:标准化直播带岗流程(车间+宿舍+食堂三景展示)'],
- tags: ['直播带岗', '老铁信任', '零门槛求职'],
- },
- price: {
- title: '价格策略:求职者免费 × 企业端付费',
- points: ['求职者端:完全免费(零门槛核心优势)', '企业端:按效果付费(CPM/CPC/简历投递量)', '主播端:带岗主播抽佣机制'],
- tags: ['零门槛', '效果付费', 'C端免费'],
- },
- place: {
- title: '渠道策略:B端入驻 + C端自然渗透',
- points: ['B端:24万家入驻用人单位,提高企业直招占比', '知名企业合作(创维/顺丰/伯恩光学)', 'C端:2.5亿蓝领月活用户自然覆盖', '40万+专职带岗主播'],
- tags: ['24万企业', '2.5亿用户', '40万主播'],
- },
- promotion: {
- title: '促销策略:情绪价值 × 信任传播',
- points: ['品牌传播:"10个蓝领9个用快手" / "直播带岗,眼见为实"', '内容营销:《来活了老铁!》综艺IP+短剧营销', '政策背书:直播招聘师认证+人社部认可', '老铁文化:"来了就是兄弟"+"有尊严地赚钱"'],
- tags: ['综艺IP', '老铁文化', '政策背书'],
- },
- };
- 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 barChart(rows, accent, maxW = 900) {
- const max = Math.max(...rows.map(r => r.heat), 1);
- const barH = 36, gap = 14, padL = 120, padR = 120, padV = 16, W = maxW;
- const totalH = rows.length * (barH + gap) + padV * 2;
- return `<div style="background:var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:20px 24px;margin-bottom:24px;">
- <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.heat / max) * (W - padL - padR), 6);
- return `<text x="${padL - 10}" 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="${r.color || accent}" opacity="0.85"/>
- <text x="${padL + w + 10}" y="${y + barH / 2 + 5}" font-size="13" font-weight="700" fill="${r.color || accent}" font-family="monospace">${esc(r.heat.toLocaleString())}</text>`;
- }).join('')}
- </svg>
- </div>`;
- }
- function careBarChart(rows, maxW = 800) {
- const max = Math.max(...rows.map(r => r.pct), 1);
- const barH = 36, gap = 14, padL = 140, padR = 100, padV = 16, W = maxW;
- const totalH = rows.length * (barH + gap) + padV * 2;
- return `<div style="background:var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:20px 24px;">
- <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.pct / 100) * (W - padL - padR), 8);
- return `<text x="${padL - 10}" 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="${r.color}" opacity="0.85"/>
- <text x="${padL + w + 10}" y="${y + barH / 2 + 5}" font-size="13" font-weight="700" fill="${r.color}" font-family="monospace">${r.pct}%</text>`;
- }).join('')}
- </svg>
- </div>`;
- }
- function competitorBarChart() {
- const W = 800, barH = 48, gap = 20, padV = 20;
- const rows = COMPETITORS;
- const max = 25000;
- const totalH = rows.length * (barH + gap) + padV * 2;
- return `<div style="background:var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:20px 24px;margin-bottom:24px;">
- <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 barW = r.users === '规模下滑' || r.users === '约3000万' ? (r.users === '规模下滑' ? 60 : (3000 / max) * (W - 160 - 120)) : (25000 / max) * (W - 160 - 120);
- return `<text x="150" y="${y + barH / 2 + 5}" text-anchor="end" font-size="14" font-weight="600" fill="${r.color}">${esc(r.name)}</text>
- <rect x="160" y="${y}" width="${Math.max(barW, 8)}" height="${barH}" rx="4" fill="${r.barColor}" opacity="0.85"/>
- <text x="${160 + Math.max(barW, 8) + 10}" y="${y + barH / 2 + 5}" font-size="13" font-weight="700" fill="${r.barColor}" font-family="monospace">${esc(r.users)}</text>
- <text x="160" y="${y + barH + 14}" font-size="11" fill="var(--text-3)">${esc(r.type)} · ${esc(r.trend)}</text>`;
- }).join('')}
- </svg>
- </div>`;
- }
- function radarChart() {
- const cx = 200, cy = 190, r = 130;
- const labels = RADAR_DATA.map(d => d.label);
- const ksVals = RADAR_DATA.map(d => d.ks);
- const bossVals = RADAR_DATA.map(d => d.boss);
- const wuVals = RADAR_DATA.map(d => d.wu);
- const n = labels.length;
- const step = 2 * Math.PI / n;
- const toXY = (v, i, R = r) => ({ x: cx + R * v * Math.cos(i * step - Math.PI / 2), y: cy + R * v * Math.sin(i * step - Math.PI / 2) });
- const ksPath = ksVals.map((v, i) => toXY(v / 5, i)).map((p, i) => (i === 0 ? 'M' : 'L') + p.x + ',' + p.y).join('') + 'Z';
- const bossPath = bossVals.map((v, i) => toXY(v / 5, i)).map((p, i) => (i === 0 ? 'M' : 'L') + p.x + ',' + p.y).join('') + 'Z';
- const wuPath = wuVals.map((v, i) => toXY(v / 5, i)).map((p, i) => (i === 0 ? 'M' : 'L') + p.x + ',' + p.y).join('') + 'Z';
- const gridPath = [1, 0.5, 0.25].map(s => [0, 1, 2, 3, 4].map(i => toXY(s, i)).map((p, i) => (i === 0 ? 'M' : 'L') + p.x + ',' + p.y).join('') + 'Z').join('');
- let labelHTML = labels.map((l, i) => {
- const p = toXY(1.15, i);
- return `<text x="${p.x}" y="${p.y}" text-anchor="middle" dominant-baseline="middle" font-size="10" fill="var(--text-2)">${esc(l)}</text>`;
- }).join('');
- return `<div style="background:var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:20px;">
- <svg viewBox="0 0 400 380" style="width:100%;height:auto;">
- <circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="var(--border)" stroke-width="1"/>
- <path d="${gridPath}" fill="none" stroke="var(--border)" stroke-width="0.5" opacity="0.5"/>
- <path d="${wuPath}" fill="rgba(107,107,107,0.15)" stroke="#6B6B6B" stroke-width="2"/>
- <path d="${bossPath}" fill="rgba(59,130,246,0.15)" stroke="#3B82F6" stroke-width="2"/>
- <path d="${ksPath}" fill="rgba(0,220,130,0.2)" stroke="#00DC82" stroke-width="2.5"/>
- ${labelHTML}
- <g transform="translate(${cx + r + 20}, 20)">
- <rect x="0" y="0" width="12" height="12" fill="#00DC82"/><text x="18" y="10" font-size="11" fill="var(--text-2)">快手</text>
- <rect x="0" y="25" width="12" height="12" fill="#3B82F6"/><text x="18" y="35" font-size="11" fill="var(--text-2)">BOSS直聘</text>
- <rect x="0" y="50" width="12" height="12" fill="#6B6B6B"/><text x="18" y="60" font-size="11" fill="var(--text-2)">58同城</text>
- </g>
- </svg>
- </div>`;
- }
- function voiceCard(comment, isHot = false) {
- const borderColor = isHot ? 'var(--amber)' : 'var(--rose)';
- const tagBg = isHot ? 'rgba(245,166,35,0.1)' : 'rgba(255,77,141,0.1)';
- const tagColor = isHot ? 'var(--amber)' : 'var(--rose)';
- return `<div class="voice-card ${isHot ? 'hot' : ''}" style="border-left:3px solid ${borderColor};">
- <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
- <span style="width:22px;height:22px;border-radius:50%;background:var(--bg-2);color:var(--text-1);font-size:0.6rem;font-weight:700;display:flex;align-items:center;justify-content:center;">快</span>
- <span style="font-size:0.8rem;color:var(--text-3);">快手用户</span>
- <span style="margin-left:auto;font-size:0.78rem;font-weight:600;color:${isHot ? 'var(--amber)' : 'var(--rose)'};">♥ ${fmtLikes(comment.likes)}</span>
- </div>
- <div class="voice-content">"${esc(comment.expr)}"</div>
- <div class="voice-meta">
- <span class="voice-tag" style="background:${tagBg};color:${tagColor};">${esc(comment.kw)}</span>
- </div>
- </div>
- `;}
- function insightBanner(num, title, body, dataPoints) {
- const dps = dataPoints.map(dp => `<div class="dp"><div class="num">${esc(dp.num)}</div><div class="lbl">${esc(dp.lbl)}</div></div>`).join('');
- return `<div class="insight-banner" data-num="${num}">
- <h3>${title}</h3>
- <p class="insight-body">${body}</p>
- <div class="insight-data">${dps}</div>
- </div>`;
- }
- function hypothesisBarChart() {
- const max = Math.max(...HYPOTHESES.map(h => h.count));
- const barH = 28, gap = 12, padL = 100, padR = 100, padV = 16, W = 800;
- const totalH = HYPOTHESES.length * (barH + gap) + padV * 2;
- return `<div style="background:var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:20px 24px;">
- <div style="min-width:520px;">
- <svg viewBox="0 0 ${W} ${totalH}" style="width:100%;height:auto;" preserveAspectRatio="xMidYMid meet">
- ${HYPOTHESES.map((h, i) => {
- const y = padV + i * (barH + gap);
- const w = Math.max((h.count / max) * (W - padL - padR), 8);
- return `<text x="${padL - 10}" y="${y + barH / 2 + 5}" text-anchor="end" font-size="11" fill="var(--text-2)">${esc(h.h)}</text>
- <rect x="${padL}" y="${y}" width="${w}" height="${barH}" rx="4" fill="${h.color}" opacity="0.85"/>
- <text x="${padL + w + 8}" y="${y + barH / 2 + 5}" font-size="11" font-weight="700" fill="${h.color}">${h.count.toLocaleString()}</text>`;
- }).join('')}
- </svg>
- </div>
- </div>`;
- }
- function fourPGrid() {
- const p = FOUR_P;
- const items = [
- { title: p.product.title, points: p.product.points, tags: p.product.tags, color: 'var(--blue)' },
- { title: p.price.title, points: p.price.points, tags: p.price.tags, color: 'var(--green)' },
- { title: p.place.title, points: p.place.points, tags: p.place.tags, color: 'var(--amber)' },
- { title: p.promotion.title, points: p.promotion.points, tags: p.promotion.tags, color: 'var(--purple)' },
- ];
- return `<div class="grid grid-2">${items.map(item => `
- <div class="card" style="border-top:3px solid ${item.color};">
- <div class="text-h3" style="color:${item.color};margin-bottom:14px;">${esc(item.title)}</div>
- <div style="font-size:0.85rem;color:var(--text-2);line-height:1.8;margin-bottom:14px;">
- ${item.points.map(pt => `<div style="margin-bottom:6px;">• ${esc(pt)}</div>`).join('')}
- </div>
- <div style="display:flex;gap:8px;flex-wrap:wrap;">
- ${item.tags.map(t => `<span class="tag" style="background:${item.color}22;color:${item.color};border-color:${item.color}44;">${esc(t)}</span>`).join('')}
- </div>
- </div>`).join('')}</div>`;
- }
- 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 深度洞察报告 v7</title>
- <style>
- :root{--bg-0:#0A0A0A;--bg-1:#111111;--bg-2:#1A1A1A;--bg-3:#252525;--border:rgba(255,255,255,0.08);--border-strong:rgba(255,255,255,0.16);--text-1:#F5F5F5;--text-2:#A8A8A8;--text-3:#6B6B6B;--amber:#F5A623;--amber-glow:rgba(245,166,35,0.15);--green:#00DC82;--purple:#8B5CF6;--rose:#FF4D8D;--blue:#3B82F6;--red:#EF4444;--orange:#F97316;--font-head:'Inter','Noto Sans SC','PingFang SC','Microsoft YaHei',sans-serif;--font-body:'Noto Sans SC','Inter','PingFang SC','Microsoft YaHei',sans-serif;--font-mono:'JetBrains Mono','SF Mono','Fira Code',monospace;--max-w:1280px;--radius-sm:8px;--radius-md:12px;--radius-lg:20px;}
- *{margin:0;padding:0;box-sizing:border-box}
- html{scroll-behavior:smooth;font-size:16px;scroll-snap-type:y proximity;}
- body{font-family:var(--font-body);background:var(--bg-0);color:var(--text-1);line-height:1.65;-webkit-font-smoothing:antialiased;overflow-x:hidden}
- body::before{content:'';position:fixed;inset:0;pointer-events:none;z-index:0;background:radial-gradient(ellipse 900px 700px at var(--amb-x,20%) var(--amb-y,25%),rgba(245,166,35,0.10),transparent 60%),radial-gradient(ellipse 800px 600px at calc(100% - var(--amb-x,20%)) calc(100% - var(--amb-y,25%)),rgba(139,92,246,0.07),transparent 60%);transition:background 1.4s cubic-bezier(.2,.7,.2,1);}
- body::after{content:'';position:fixed;inset:0;pointer-events:none;z-index:0;background-image:linear-gradient(rgba(255,255,255,0.015) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.015) 1px,transparent 1px);background-size:80px 80px;mask-image:radial-gradient(ellipse 90% 95% at 50% 50%,black 0%,transparent 85%);-webkit-mask-image:radial-gradient(ellipse 90% 95% at 50% 50%,black 0%,transparent 85%);}
- main.deck{position:relative;z-index:1}
- ::selection{background:var(--amber);color:#000}
- .text-hero{font-family:var(--font-head);font-weight:900;letter-spacing:-0.04em;line-height:0.95;font-size:clamp(4rem,12vw,10rem)}
- .text-poster{font-family:var(--font-head);font-weight:800;letter-spacing:-0.03em;line-height:1.05;font-size:clamp(2.5rem,6vw,5rem)}
- .text-h1{font-family:var(--font-head);font-weight:700;letter-spacing:-0.02em;line-height:1.1;font-size:clamp(2rem,4vw,3.5rem)}
- .text-h2{font-family:var(--font-head);font-weight:700;letter-spacing:-0.015em;line-height:1.2;font-size:clamp(1.4rem,2.5vw,2rem)}
- .text-h3{font-weight:600;font-size:clamp(1.05rem,1.6vw,1.3rem);letter-spacing:-0.01em}
- .text-body{font-size:clamp(0.92rem,1.1vw,1rem);line-height:1.75;color:var(--text-2)}
- .text-sm{font-size:0.85rem;color:var(--text-2)}
- .text-xs{font-size:0.72rem;color:var(--text-3);letter-spacing:0.02em}
- .text-mono{font-family:var(--font-mono)}
- .text-amber{color:var(--amber)}.text-green{color:var(--green)}.text-purple{color:var(--purple)}.text-rose{color:var(--rose)}.text-blue{color:var(--blue)}.text-red{color:var(--red)}.text-orange{color:var(--orange)}
- section.report-section{min-height:100vh;padding:clamp(60px,10vh,120px) clamp(24px,6vw,80px);position:relative;border-bottom:1px solid var(--border);display:flex;flex-direction:column;justify-content:center;scroll-snap-align:start;scroll-snap-stop:always;overflow:hidden;isolation:isolate}
- section.report-section::before{content:attr(data-slide-num);position:absolute;top:clamp(20px,3vh,36px);right:clamp(24px,4vw,60px);font-family:var(--font-mono);font-weight:600;font-size:clamp(0.68rem,0.75vw,0.78rem);letter-spacing:0.25em;color:var(--text-3);z-index:4;pointer-events:none}
- section.report-section::after{content:'';position:absolute;inset:0;pointer-events:none;z-index:-1;background:radial-gradient(ellipse 70% 60% at 85% 15%,var(--slide-glow-1,rgba(245,166,35,0.06)),transparent 55%),radial-gradient(ellipse 60% 50% at 15% 85%,var(--slide-glow-2,rgba(139,92,246,0.04)),transparent 55%);opacity:0;transition:opacity 1s cubic-bezier(.2,.7,.2,1)}
- section.report-section.in-view::after{opacity:1}
- section.report-section[data-tone="amber"]{--slide-glow-1:rgba(245,166,35,0.10);--slide-glow-2:rgba(245,166,35,0.04)}
- section.report-section[data-tone="rose"]{--slide-glow-1:rgba(255,77,141,0.09);--slide-glow-2:rgba(255,77,141,0.04)}
- section.report-section[data-tone="green"]{--slide-glow-1:rgba(0,220,130,0.08);--slide-glow-2:rgba(0,220,130,0.03)}
- section.report-section[data-tone="purple"]{--slide-glow-1:rgba(139,92,246,0.10);--slide-glow-2:rgba(139,92,246,0.04)}
- section.report-section[data-tone="blue"]{--slide-glow-1:rgba(59,130,246,0.08);--slide-glow-2:rgba(59,130,246,0.03)}
- section.report-section[data-tone="orange"]{--slide-glow-1:rgba(249,115,22,0.10);--slide-glow-2:rgba(249,115,22,0.04)}
- .cover-hero{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;padding:clamp(40px,8vh,100px) clamp(24px,6vw,80px);background:radial-gradient(ellipse 60% 80% at 30% 30%,rgba(245,166,35,0.08) 0%,transparent 70%),radial-gradient(ellipse 40% 60% at 80% 70%,rgba(139,92,246,0.05) 0%,transparent 70%),var(--bg-0)}
- .cover-grid-bg{position:absolute;inset:0;background-image:linear-gradient(rgba(255,255,255,0.025) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.025) 1px,transparent 1px);background-size:60px 60px;mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 0%,transparent 70%);-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 0%,transparent 70%)}
- .cover-content{position:relative;z-index:2;max-width:var(--max-w)}
- .cover-meta{display:flex;align-items:center;gap:16px;font-family:var(--font-mono);font-size:0.78rem;color:var(--text-3);letter-spacing:0.18em;text-transform:uppercase;margin-bottom:32px}
- .cover-meta::before{content:'';width:32px;height:2px;background:var(--amber)}
- .cover-title-stack{display:flex;flex-direction:column;gap:4px}
- .cover-title{font-family:var(--font-head);font-weight:900;letter-spacing:-0.04em;line-height:0.92;font-size:clamp(3.5rem,11vw,9rem);color:#fff;background:linear-gradient(135deg,#fff 30%,rgba(245,166,35,0.9) 100%);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
- .cover-sub{font-size:clamp(1rem,1.6vw,1.4rem);color:var(--text-2);max-width:780px;margin-top:32px;line-height:1.7}
- .cover-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(12px,2vw,28px);margin-top:clamp(48px,8vh,96px);padding-top:clamp(24px,4vh,48px);border-top:1px solid var(--border);max-width:900px}
- .cover-stat .num{font-family:var(--font-head);font-weight:800;font-size:clamp(1.6rem,2.8vw,2.4rem);letter-spacing:-0.02em;color:var(--amber)}
- .cover-stat .lbl{font-size:0.78rem;color:var(--text-3);margin-top:4px;font-family:var(--font-mono);letter-spacing:0.05em;text-transform:uppercase}
- @media(max-width:720px){.cover-stats{grid-template-columns:repeat(2,1fr)}}
- .nav-rail{position:fixed;left:clamp(16px,2vw,32px);top:50%;transform:translateY(-50%);z-index:100;display:flex;flex-direction:column;gap:12px}
- .nav-dot{width:32px;height:3px;border-radius:2px;background:rgba(255,255,255,0.14);cursor:pointer;transition:all 0.5s cubic-bezier(.2,.7,.2,1);position:relative;display:flex;align-items:center}
- .nav-dot.active{background:var(--amber);width:48px;box-shadow:0 0 12px rgba(245,166,35,0.4)}
- .nav-dot:hover{background:rgba(255,255,255,0.3)}
- .nav-dot::after{content:attr(data-tip);position:absolute;left:calc(100% + 16px);top:50%;transform:translateY(-50%) translateX(-6px);background:rgba(20,20,20,0.92);backdrop-filter:blur(8px);border:1px solid var(--border);padding:6px 12px;border-radius:6px;font-size:0.72rem;color:var(--text-1);white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 0.25s ease,transform 0.25s ease;font-family:var(--font-mono);letter-spacing:0.04em}
- .nav-dot:hover::after,.nav-dot.active::after{opacity:1;transform:translateY(-50%) translateX(0)}
- .progress-line{position:fixed;top:0;left:0;height:2px;background:linear-gradient(90deg,var(--amber),var(--rose));z-index:1000;width:0%;transition:width 0.3s ease}
- .top-bar{position:fixed;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:center;padding:18px clamp(24px,4vw,48px);z-index:99;pointer-events:none;background:linear-gradient(180deg,rgba(10,10,10,0.85) 0%,transparent 100%)}
- .brand-logo{font-family:var(--font-head);font-weight:900;font-size:0.88rem;letter-spacing:0.2em}
- .brand-logo .dot{color:var(--amber)}
- .slide-counter{position:fixed;left:clamp(16px,2vw,28px);bottom:clamp(16px,2vh,28px);z-index:100;display:flex;align-items:center;gap:8px;padding:8px 14px;border-radius:999px;background:rgba(20,20,20,0.85);backdrop-filter:blur(16px);border:1px solid var(--border-strong);font-family:var(--font-mono);font-size:0.74rem;letter-spacing:0.1em;color:var(--text-2)}
- .slide-counter .now{color:var(--amber);font-weight:700}
- .slide-counter .bar{width:60px;height:2px;background:rgba(255,255,255,0.1);border-radius:2px;overflow:hidden}
- .slide-counter .bar::after{content:'';display:block;height:100%;width:var(--pct,0%);background:linear-gradient(90deg,var(--amber),var(--rose));transition:width 0.6s cubic-bezier(.2,.7,.2,1)}
- .kbd-hint{position:fixed;right:clamp(16px,2vw,28px);bottom:clamp(16px,2vh,28px);z-index:100;display:flex;align-items:center;gap:10px;padding:10px 16px;border-radius:999px;background:rgba(20,20,20,0.85);backdrop-filter:blur(16px);border:1px solid var(--border-strong);font-family:var(--font-mono);font-size:0.72rem;color:var(--text-2);letter-spacing:0.04em;transition:opacity 0.6s ease,transform 0.6s ease}
- .kbd-hint.fade{opacity:0;transform:translateY(12px);pointer-events:none}
- .kbd-hint kbd{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;padding:0 6px;background:var(--bg-3);border:1px solid var(--border-strong);border-radius:4px;font-size:0.7rem;font-family:var(--font-mono);color:var(--text-1);box-shadow:0 1px 0 rgba(0,0,0,0.4)}
- .section-inner{width:100%;max-width:var(--max-w);margin:0 auto;position:relative}
- .section-head{margin-bottom:clamp(32px,5vh,64px)}
- .section-eyebrow{display:inline-flex;align-items:center;gap:10px;font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.18em;text-transform:uppercase;color:var(--amber);font-weight:500;margin-bottom:18px}
- .section-eyebrow::before{content:'';display:inline-block;width:24px;height:1px;background:var(--amber)}
- .section-sub{color:var(--text-2);font-size:1.05rem;max-width:720px;margin-top:14px;line-height:1.8}
- .grid{display:grid;gap:clamp(14px,1.6vw,22px)}
- .grid-2{grid-template-columns:repeat(auto-fit,minmax(320px,1fr))}
- .grid-3{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
- .grid-4{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
- .card{background:linear-gradient(180deg,rgba(255,255,255,0.025) 0%,rgba(255,255,255,0.008) 100%),var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:clamp(20px,2.2vw,32px);position:relative;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);transition:transform 0.4s cubic-bezier(.2,.7,.2,1),border-color 0.3s ease,box-shadow 0.4s ease}
- .card:hover{border-color:var(--border-strong);transform:translateY(-3px);box-shadow:0 12px 40px rgba(0,0,0,0.35),0 0 0 1px rgba(245,166,35,0.08)}
- .voice-card:hover{transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,0.3);border-color:var(--border-strong);}
- .card.card-glow{box-shadow:0 0 60px rgba(245,166,35,0.12),0 0 120px rgba(245,166,35,0.06)}
- .card.card-glow-green{box-shadow:0 0 60px rgba(0,220,130,0.12),0 0 120px rgba(0,220,130,0.06)}
- .card.card-glow-rose{box-shadow:0 0 60px rgba(255,77,141,0.12),0 0 120px rgba(255,77,141,0.06)}
- .card-accent{border-left:3px solid var(--amber);border-top:1px solid var(--border);border-right:1px solid var(--border);border-bottom:1px solid var(--border)}
- .metric{background:var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:clamp(24px,2.5vw,36px);position:relative;overflow:hidden}
- .metric::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,var(--amber),transparent);opacity:0.6}
- .metric .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 .metric-value{font-family:var(--font-head);font-weight:800;font-size:clamp(2.2rem,4vw,3.5rem);letter-spacing:-0.03em;line-height:1;color:var(--text-1);margin-bottom:8px}
- .metric .metric-unit{font-family:var(--font-mono);font-size:0.85rem;color:var(--amber);font-weight:500}
- .metric .metric-sub{margin-top:14px;font-size:0.82rem;color:var(--text-2);line-height:1.5}
- .voice-card{background:var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:20px 24px;margin-bottom:14px;transition:all 0.3s ease;position:relative;overflow:visible}
- .voice-card::before{content:'';position:absolute;top:0;bottom:0;left:0;width:3px;height:auto;background:var(--rose);opacity:0.6;transition:opacity 0.3s;border-radius:var(--radius-md) 0 0 var(--radius-md);}
- .voice-card.hot{border-color:rgba(245,166,35,0.3)}
- .voice-card.hot::before{opacity:1;background:var(--amber)}
- .voice-card:hover{border-color:var(--border-strong);transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,0.3)}
- .voice-card .voice-content{font-size:0.96rem;line-height:1.7;color:var(--text-1);margin:12px 0;font-weight:400}
- .voice-card .voice-meta{display:flex;gap:8px;flex-wrap:wrap}
- .voice-tag{display:inline-flex;padding:3px 10px;background:rgba(255,77,141,0.1);color:var(--rose);border-radius:4px;font-size:0.72rem;font-weight:500}
- .voice-card.hot .voice-tag{background:rgba(245,166,35,0.1);color:var(--amber)}
- .tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:4px;font-size:0.72rem;font-weight:500;letter-spacing:0.02em;font-family:var(--font-head);background:rgba(245,166,35,0.1);color:var(--amber);border:1px solid rgba(245,166,35,0.22)}
- .tag.green{background:rgba(0,220,130,0.08);color:var(--green);border-color:rgba(0,220,130,0.2)}
- .tag.purple{background:rgba(139,92,246,0.08);color:var(--purple);border-color:rgba(139,92,246,0.2)}
- .tag.rose{background:rgba(255,77,141,0.08);color:var(--rose);border-color:rgba(255,77,141,0.2)}
- .tag.blue{background:rgba(59,130,246,0.08);color:var(--blue);border-color:rgba(59,130,246,0.2)}
- .tag.orange{background:rgba(249,115,22,0.08);color:var(--orange);border-color:rgba(249,115,22,0.2)}
- .tag.red{background:rgba(239,68,68,0.08);color:var(--red);border-color:rgba(239,68,68,0.2)}
- .cover-hero .aurora{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:1}
- .cover-hero .aurora span{position:absolute;border-radius:50%;filter:blur(80px);opacity:0.5}
- .cover-hero .aurora span:nth-child(1){width:500px;height:500px;background:rgba(245,166,35,0.35);top:-10%;left:10%;animation:aurora-drift-1 18s ease-in-out infinite alternate}
- .cover-hero .aurora span:nth-child(2){width:420px;height:420px;background:rgba(139,92,246,0.30);bottom:-8%;right:10%;animation:aurora-drift-2 22s ease-in-out infinite alternate}
- .cover-hero .aurora span:nth-child(3){width:300px;height:300px;background:rgba(0,220,130,0.22);top:40%;left:55%;animation:aurora-drift-3 26s ease-in-out infinite alternate}
- @keyframes aurora-drift-1{from{transform:translate(0,0) scale(1)}to{transform:translate(60px,80px) scale(1.2)}}
- @keyframes aurora-drift-2{from{transform:translate(0,0) scale(1)}to{transform:translate(-80px,-60px) scale(1.15)}}
- @keyframes aurora-drift-3{from{transform:translate(0,0) scale(1)}to{transform:translate(-100px,40px) scale(1.3)}}
- @media(prefers-reduced-motion:reduce){.cover-hero .aurora span{animation:none}}
- .insight-banner{min-height:260px;background:var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:clamp(32px,4vw,56px);display:flex;flex-direction:column;justify-content:space-between;gap:32px;position:relative;overflow:hidden;margin-bottom:28px}
- .insight-banner::before{content:attr(data-num);position:absolute;top:16px;right:24px;font-family:var(--font-head);font-weight:900;font-size:clamp(4rem,9vw,7rem);line-height:1;color:var(--bg-3);letter-spacing:-0.05em;pointer-events:none}
- .insight-banner h3{font-family:var(--font-head);font-weight:800;font-size:clamp(1.5rem,2.8vw,2.2rem);letter-spacing:-0.02em;line-height:1.25;color:var(--text-1);max-width:85%}
- .insight-banner h3 strong,.insight-banner h3 span{color:var(--amber);font-weight:900}
- .insight-banner .insight-body{color:var(--text-2);font-size:1rem;line-height:1.75;max-width:85%}
- .insight-banner .insight-data{display:flex;gap:24px;margin-top:16px;padding-top:20px;border-top:1px solid var(--border)}
- .insight-banner .insight-data .dp{display:flex;flex-direction:column;gap:2px}
- .insight-banner .insight-data .num{font-family:var(--font-mono);font-size:1.3rem;font-weight:700;color:var(--amber)}
- .insight-banner .insight-data .lbl{font-size:0.72rem;color:var(--text-3);text-transform:uppercase;letter-spacing:0.1em}
- .data-tbl{width:100%;border-collapse:collapse;font-size:0.88rem}
- .data-tbl thead th{text-align:left;padding:14px 16px;background:var(--bg-1);border-bottom:1px solid var(--border);font-family:var(--font-mono);font-weight:500;font-size:0.72rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--text-3)}
- .data-tbl tbody td{padding:14px 16px;border-bottom:1px solid var(--border);color:var(--text-1);vertical-align:middle}
- .data-tbl tbody tr:last-child td{border-bottom:none}
- .data-tbl tbody tr:hover{background:rgba(255,255,255,0.02)}
- .data-tbl .tb-num{font-family:var(--font-mono);font-weight:600;color:var(--amber)}
- .table-wrap{overflow-x:auto;border-radius:var(--radius-md);border:1px solid var(--border)}
- .theme-poster{background:var(--bg-1);border:1px solid var(--border);border-radius:var(--radius-md);padding:28px;position:relative;overflow:hidden}
- .theme-poster.pain{border-top:3px solid var(--rose)}
- .theme-poster.bright{border-top:3px solid var(--green)}
- .theme-poster.scene{border-top:3px solid var(--blue)}
- .theme-poster h4{font-family:var(--font-head);font-weight:700;font-size:1.3rem;margin-bottom:8px}
- .theme-poster .count{font-family:var(--font-mono);font-size:0.78rem;color:var(--text-3);margin-bottom:16px}
- .theme-poster .count .num{font-size:1.4rem;font-weight:700;color:var(--text-1);margin-right:4px}
- .theme-poster .examples{list-style:none;display:flex;flex-direction:column;gap:10px;margin-top:12px}
- .theme-poster .examples li{padding:10px 14px;background:var(--bg-2);border-left:2px solid var(--border-strong);border-radius:4px;font-size:0.85rem;line-height:1.55;color:var(--text-2)}
- .theme-poster.pain .examples li{border-left-color:var(--rose)}
- .theme-poster.bright .examples li{border-left-color:var(--green)}
- .theme-poster.scene .examples li{border-left-color:var(--blue)}
- .fade-up{opacity:1;transform:none;transition:opacity 0.9s cubic-bezier(.16,.84,.33,1),transform 0.9s cubic-bezier(.16,.84,.33,1)}
- html.js .fade-up{opacity:0;transform:translateY(32px);will-change:opacity,transform}
- html.js .fade-up.is-visible{opacity:1;transform:translateY(0)}
- footer{text-align:center;padding:48px clamp(24px,6vw,80px);border-top:1px solid var(--border);color:var(--text-3);font-size:0.8rem;background:var(--bg-0)}
- footer .meta{font-family:var(--font-mono);letter-spacing:0.12em;text-transform:uppercase;margin-top:8px;font-size:0.7rem}
- @media(max-width:900px){.nav-rail{display:none}}
- </style>
- </head>
- <body>
- <div class="progress-line" id="progressLine"></div>
- <header class="top-bar"><div class="brand-logo">快手快聘<span class="dot">·</span>VOC</div></header>
- <nav class="nav-rail" id="navRail"></nav>
- <div class="slide-counter"><span class="now" id="slideNow">01</span><div class="bar"><div id="slideBar"></div></div><span id="slideTotal">09</span></div>
- <div class="kbd-hint"><kbd>↓</kbd><kbd>↑</kbd> 滚动切换</div>
- <main class="deck">
- <!-- 封面 -->
- <section class="report-section cover-hero" data-tone="amber" style="min-height:100vh;">
- <div class="cover-grid-bg"></div>
- <div class="aurora"><span></span><span></span><span></span></div>
- <div class="cover-content">
- <div class="cover-meta">VOC 深度洞察报告 · v7</div>
- <div class="cover-title-stack">
- <div class="cover-title">快手快聘</div>
- </div>
- <p class="cover-sub">"10个蓝领,9个都用快手"——蓝领求职生态与快聘战略洞察</p>
- <div class="cover-stats">
- ${COVER_STATS.map(s => `<div class="cover-stat"><div class="num">${esc(s.num)}</div><div class="lbl">${esc(s.lbl)}</div></div>`).join('')}
- </div>
- </div>
- </section>
- <!-- Ch1: 执行摘要 -->
- <section class="report-section" data-slide-num="01" data-tone="amber" id="ch1">
- <div class="section-inner">
- <div class="section-head">
- <div class="section-eyebrow">CHAPTER 01</div>
- <h2 class="text-h1">执行摘要</h2>
- <p class="section-sub">核心问题与机会分析</p>
- </div>
- <div class="grid grid-4" style="margin-bottom:32px;">
- ${KPIS.map(k => `<div class="metric"><div class="metric-label">${esc(k.label)}</div><div class="metric-value" style="color:${k.color};">${esc(k.value)}</div><div class="metric-sub">${esc(k.sub)}</div></div>`).join('')}
- </div>
- <div class="card card-glow card-accent" style="margin-bottom:28px;padding:28px 32px;">
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--amber);margin-bottom:14px;font-weight:600;">核心洞察</div>
- <div class="text-h2" style="margin-bottom:12px;">蓝领求职最担心什么?最看重什么?</div>
- <p class="text-body">最强焦虑不是"工资低",而是"怕被骗"——信息不真实、被坑、黑中介、押金不退、工资被克扣</p>
- </div>
- <div class="grid grid-3">
- <div class="card" style="border-left:3px solid var(--rose);">
- <div style="font-size:1.6rem;margin-bottom:12px;">🛡️</div>
- <div style="font-weight:700;margin-bottom:8px;">最强焦虑:被骗</div>
- <p class="text-sm">"骗/黑中介/坑"相关风险词热度突破100万,VOC占比最高</p>
- </div>
- <div class="card" style="border-left:3px solid var(--green);">
- <div style="font-size:1.6rem;margin-bottom:12px;">💰</div>
- <div style="font-weight:700;margin-bottom:8px;">最强诉求:确定性</div>
- <p class="text-sm">工资真实、发薪稳定、包吃包住、当天入职、能快速安顿</p>
- </div>
- <div class="card" style="border-left:3px solid var(--purple);">
- <div style="font-size:1.6rem;margin-bottom:12px;">🤝</div>
- <div style="font-weight:700;margin-bottom:8px;">情绪核心:信任</div>
- <p class="text-sm">"老铁文化"+"眼见为实"是快聘最难被竞品复制的竞争壁垒</p>
- </div>
- </div>
- </div>
- </section>
- <!-- Ch2: 快聘生态现状 -->
- <section class="report-section" data-slide-num="02" data-tone="orange" id="ch2">
- <div class="section-inner">
- <div class="section-head">
- <div class="section-eyebrow" style="color:var(--orange);">CHAPTER 02</div>
- <h2 class="text-h1">快聘生态现状</h2>
- <p class="section-sub">市场规模、竞争格局与战略位势</p>
- </div>
- <div class="grid grid-4" style="margin-bottom:32px;">
- ${KUAIKUAI_DATA.map(d => `<div class="metric"><div class="metric-label">${esc(d.label)}</div><div class="metric-value" style="color:var(--green);">${esc(d.value)}</div><div class="metric-sub">${esc(d.note)}</div></div>`).join('')}
- </div>
- <div class="card card-glow" style="border-left:3px solid var(--green);margin-bottom:28px;padding:28px 32px;">
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--green);margin-bottom:14px;font-weight:600;">战略定位</div>
- <div class="text-h2" style="margin-bottom:12px;">"10个蓝领9个用快手——直播带岗,眼见为实"</div>
- <p class="text-body">快聘日活几十万简历,内部仅次于电商;2.5亿蓝领月活是最大壁垒;但信息审核弱是最大短板</p>
- </div>
- <div class="grid grid-3" style="margin-bottom:24px;">
- ${COMPETITORS.map(c => `
- <div class="card" style="border-top:3px solid ${c.color};">
- <div style="font-weight:700;color:${c.color};margin-bottom:10px;">${esc(c.name)}</div>
- <div style="font-size:0.82rem;color:var(--text-2);line-height:1.7;">
- <div style="margin-bottom:6px;"><span style="color:var(--green);">✓</span> ${esc(c.strength)}</div>
- <div style="margin-bottom:6px;"><span style="color:var(--green);">✓</span> 用户:${esc(c.users)} · ${esc(c.type)}</div>
- <div><span style="color:var(--rose);">⚠</span> ${esc(c.weak)}</div>
- </div>
- </div>`).join('')}
- </div>
- <div class="fade-up">
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--orange);margin-bottom:14px;font-weight:600;">📊 平台竞争要素雷达图</div>
- ${radarChart()}
- </div>
- </div>
- </section>
- <!-- Ch3: 蓝领求职心理洞察 -->
- <section class="report-section" data-slide-num="03" data-tone="rose" id="ch3">
- <div class="section-inner">
- <div class="section-head">
- <div class="section-eyebrow" style="color:var(--rose);">CHAPTER 03</div>
- <h2 class="text-h1">蓝领求职心理洞察</h2>
- <p class="section-sub">最担心什么?最看重什么?</p>
- </div>
- <div class="grid grid-2" style="margin-bottom:28px;">
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--rose);margin-bottom:16px;font-weight:600;">TOP 焦虑来源(按热度)</div>
- ${barChart(RISK_SIGNALS.slice(0, 6), 'var(--rose)')}
- </div>
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--green);margin-bottom:16px;font-weight:600;">TOP 诉求来源(按热度)</div>
- ${barChart(CARE_FACTORS.slice(0, 6), 'var(--green)')}
- </div>
- </div>
- ${insightBanner('01', '蓝领求职者最需要的是<strong>确定性</strong>——确定工作是真的、<strong>工资会发</strong>、<strong>不会被坑</strong>、能快速安顿下来', '情绪价值的核心不是娱乐,而是"确定性":靠谱、真实、放心、少踩坑。', [
- { num: '85%', lbl: '担心信息不真实' },
- { num: '72%', lbl: '关注工资按时发放' },
- { num: '65%', lbl: '关注包吃包住' },
- ])}
- <div class="grid grid-3">
- <div class="card" style="border-left:3px solid var(--rose);">
- <div style="font-size:1.6rem;margin-bottom:12px;">🛡️</div>
- <div style="font-weight:700;color:var(--rose);margin-bottom:8px;">焦虑1:信息虚假</div>
- <p class="text-sm">承诺月薪近万,实际到手4000多;薪资货不对版是头号痛点</p>
- </div>
- <div class="card" style="border-left:3px solid var(--amber);">
- <div style="font-size:1.6rem;margin-bottom:12px;">💰</div>
- <div style="font-weight:700;color:var(--amber);margin-bottom:8px;">焦虑2:押金陷阱</div>
- <p class="text-sm">预缴押金/培训费后失联,押金不退是高频次焦虑</p>
- </div>
- <div class="card" style="border-left:3px solid var(--purple);">
- <div style="font-size:1.6rem;margin-bottom:12px;">🤝</div>
- <div style="font-weight:700;color:var(--purple);margin-bottom:8px;">焦虑3:权益保障</div>
- <p class="text-sm">不签合同、不上保险,维权举证难、责任界定难</p>
- </div>
- </div>
- </div>
- </section>
- <!-- Ch4: 核心关切:薪资与权益 -->
- <section class="report-section" data-slide-num="04" data-tone="amber" id="ch4">
- <div class="section-inner">
- <div class="section-head">
- <div class="section-eyebrow">CHAPTER 04</div>
- <h2 class="text-h1">核心关切:薪资与权益</h2>
- <p class="section-sub">工资、食宿、入职便捷性</p>
- </div>
- <div class="grid grid-2" style="margin-bottom:28px;">
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--amber);margin-bottom:14px;font-weight:600;">薪资权益关键词热度</div>
- ${barChart(CARE_FACTORS, 'var(--amber)')}
- </div>
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--green);margin-bottom:14px;font-weight:600;">高赞薪资VOC</div>
- ${SALARY_VOC.map(v => voiceCard(v, false)).join('\n')}
- </div>
- </div>
- ${insightBanner('02', '薪资透明度是<strong>核心关切</strong>,但最核心的购买点是"确定性"——<strong>包吃包住+当天入职+不发愁</strong>', '蓝领求职者决策链极短,追求"今天看、明天就上班"。快聘能快速集结大批求职者,春节高峰期效果尤为显著。', [
- { num: '89,190', lbl: '"包吃包住"热度' },
- { num: '89,081', lbl: '"工资"热度' },
- { num: '19,264', lbl: '"当天入职"热度' },
- ])}
- <div class="grid grid-2">
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--green);margin-bottom:14px;font-weight:600;">食宿条件VOC</div>
- ${FOOD_VOC.map(v => voiceCard(v, false)).join('\n')}
- </div>
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--rose);margin-bottom:14px;font-weight:600;">TOP10 高赞评论</div>
- ${HIGH_LIKED.slice(0, 5).map(v => voiceCard(v, v.hot)).join('\n')}
- </div>
- </div>
- </div>
- </section>
- <!-- Ch5: 风险感知:被骗焦虑 -->
- <section class="report-section" data-slide-num="05" data-tone="rose" id="ch5">
- <div class="section-inner">
- <div class="section-head">
- <div class="section-eyebrow" style="color:var(--rose);">CHAPTER 05</div>
- <h2 class="text-h1">风险感知:被骗焦虑</h2>
- <p class="section-sub">黑中介、押金陷阱、入职被套路</p>
- </div>
- <div class="grid grid-3" style="margin-bottom:28px;">
- <div class="theme-poster pain">
- <h4>黑中介 · 最强风险信号</h4>
- <div class="count">热度 <span class="num">393,762</span> · 109条命中</div>
- <ul class="examples">
- <li>"万恶之源 #黑中介的噩梦,只要我不努力,你就过不上你想要的生活" 热度203,219</li>
- <li>"#严厉打击黑中介 #黑中介骗了多少中国同胞" 热度61,984</li>
- <li>"第一次进厂是通过直播,就是黑中介..." ♥226</li>
- </ul>
- </div>
- <div class="theme-poster pain">
- <h4>押金陷阱 · 高频次焦虑</h4>
- <div class="count">热度 <span class="num">95,543</span> · 105条命中</div>
- <ul class="examples">
- <li>"不退租客押金你是心高气傲,被租客报复你是生死难料" 热度55,223</li>
- <li>"去买几个鸡蛋,打碎,放在沙发下面..." ♥2,209(TOP2高赞)</li>
- <li>"我在广西就遇到过房东死活不退押金" ♥808</li>
- </ul>
- </div>
- <div class="theme-poster pain">
- <h4>入职被坑 · 薪资货不对版</h4>
- <div class="count">热度 <span class="num">375,264</span> · 92条命中</div>
- <ul class="examples">
- <li>"刚开始签18块/时,干了1个月只拿6块/时,只拿了1200" 热度42,121</li>
- <li>"只要收身份证的都是,正常的面试只需要填表就行" ♥228</li>
- <li>"地铁安检我知道,找中介还不如直接去地铁口问" ♥224</li>
- </ul>
- </div>
- </div>
- <div class="grid grid-2">
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--rose);margin-bottom:14px;font-weight:600;">TOP10 高赞风险评论</div>
- ${HIGH_LIKED.map(v => voiceCard(v, v.hot)).join('\n')}
- </div>
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--rose);margin-bottom:14px;font-weight:600;">风险关键词完整排行</div>
- ${barChart(RISK_SIGNALS, 'var(--rose)')}
- </div>
- </div>
- </div>
- </section>
- <!-- Ch6: 平台信任机制 -->
- <section class="report-section" data-slide-num="06" data-tone="purple" id="ch6">
- <div class="section-inner">
- <div class="section-head">
- <div class="section-eyebrow" style="color:var(--purple);">CHAPTER 06</div>
- <h2 class="text-h1">平台信任机制</h2>
- <p class="section-sub">老铁文化、直播带岗、情绪关键词体系</p>
- </div>
- <div class="grid grid-3" style="margin-bottom:32px;">
- ${EMOTION_KEYWORDS.map(e => `
- <div class="card" style="border-top:3px solid ${e.color};">
- <div style="font-size:2rem;margin-bottom:12px;">${e.emoji}</div>
- <div class="text-h3" style="color:${e.color};margin-bottom:8px;">${esc(e.word)}</div>
- <p class="text-sm">${esc(e.desc)}</p>
- </div>`).join('')}
- </div>
- ${insightBanner('06', '情绪价值是<strong>双刃剑</strong>——真实故事获得关注,但<strong>虚假摆拍</strong>正在侵蚀信任基础', '外卖骑手、快递小哥、网约车司机因"接地气""高共鸣"属性,成为短视频平台虚假摆拍重灾区。平台算法无差别推荐,使虚假内容获得杠杆式传播。', [
- { num: '★★★★★', lbl: '情绪获得感优势' },
- { num: '⚠️高风险', lbl: '虚假摆拍泛滥' },
- { num: '五部门', lbl: '行政指导监管' },
- ])}
- <div class="card card-glow" style="border-left:3px solid var(--purple);padding:28px 32px;margin-bottom:28px;">
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--purple);margin-bottom:14px;font-weight:600;">标杆案例 · 快手《来活了老铁!》</div>
- <div class="text-h2" style="margin-bottom:12px;">快手联合山东卫视推出的首档蓝领求职综艺IP</div>
- <p class="text-body">25个热搜榜单(微博主榜6个),最高总榜TOP21,完成度625%。站外曝光量原定2.5亿,实际完成3.26亿,完成度130%。围绕"技能向+娱乐向+社会向"三个维度展开。</p>
- <div style="margin-top:14px;display:flex;gap:10px;">
- <span class="tag purple">技能向</span>
- <span class="tag purple">娱乐向</span>
- <span class="tag purple">社会向</span>
- </div>
- </div>
- <div class="grid grid-2">
- <div class="card" style="border-top:3px solid var(--green);">
- <div style="font-weight:700;color:var(--green);margin-bottom:14px;">老铁信任体系</div>
- <div style="font-size:0.85rem;color:var(--text-2);line-height:1.8;">
- <div style="margin-bottom:8px;">• 熟人推荐式信任</div>
- <div style="margin-bottom:8px;">• 主播与粉丝长期关系积累</div>
- <div style="margin-bottom:8px;">• 评论区实时互动答疑</div>
- <div>• "来了就是兄弟"文化</div>
- </div>
- </div>
- <div class="card" style="border-top:3px solid var(--blue);">
- <div style="font-weight:700;color:var(--blue);margin-bottom:14px;">直播带岗信任机制</div>
- <div style="font-size:0.85rem;color:var(--text-2);line-height:1.8;">
- <div style="margin-bottom:8px;">• 车间直播展示</div>
- <div style="margin-bottom:8px;">• 宿舍直播展示</div>
- <div style="margin-bottom:8px;">• 食堂直播展示</div>
- <div>• 眼见为实消除信息不对称</div>
- </div>
- </div>
- </div>
- </div>
- </section>
- <!-- Ch7: 机会与建议 -->
- <section class="report-section" data-slide-num="07" data-tone="green" id="ch7">
- <div class="section-inner">
- <div class="section-head">
- <div class="section-eyebrow" style="color:var(--green);">CHAPTER 07</div>
- <h2 class="text-h1">机会与建议</h2>
- <p class="section-sub">快聘优势强化、风险应对、战略方向</p>
- </div>
- <div class="grid grid-2" style="margin-bottom:28px;">
- ${STRATEGY_RECS.slice(0, 4).map(r => `
- <div class="card" style="border-top:3px solid ${r.color};">
- <div style="display:flex;align-items:center;gap:12px;margin-bottom:14px;">
- <div style="width:32px;height:32px;border-radius:50%;background:${r.color};color:#000;font-weight:800;display:flex;align-items:center;justify-content:center;font-size:0.9rem;">${esc(r.num)}</div>
- <div class="text-h3">${esc(r.title)}</div>
- </div>
- <p class="text-sm">${esc(r.desc)}</p>
- </div>`).join('')}
- </div>
- <div class="card" style="border-top:3px solid var(--rose);margin-bottom:28px;">
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--rose);margin-bottom:14px;font-weight:600;">⚠️ 风险矩阵</div>
- <div class="table-wrap">
- <table class="data-tbl">
- <thead><tr><th>风险类型</th><th>描述</th><th>概率</th><th>影响</th><th>应对策略</th></tr></thead>
- <tbody>
- ${RISKS.map(r => `<tr style="background:${r.color}11;">
- <td><strong style="color:${r.color};">${esc(r.type)}</strong></td>
- <td style="color:var(--text-2);font-size:0.85rem;">${esc(r.desc)}</td>
- <td style="color:${r.color};font-weight:600;">${esc(r.prob)}</td>
- <td style="color:${r.color};font-weight:600;">${esc(r.impact)}</td>
- <td style="color:var(--text-2);font-size:0.82rem;">${esc(r.strategy)}</td>
- </tr>`).join('')}
- </tbody>
- </table>
- </div>
- </div>
- <div class="card" style="border-top:3px solid var(--green);padding:28px 32px;">
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--green);margin-bottom:14px;font-weight:600;">📋 五部门行政指导要求</div>
- <div class="grid grid-2">
- <div style="font-size:0.85rem;color:var(--text-2);line-height:1.8;">
- <div style="margin-bottom:8px;">① <strong style="color:var(--text-1);">资质审核到位</strong> — 对招聘方资质进行核验</div>
- <div style="margin-bottom:8px;">② <strong style="color:var(--text-1);">信息明示到位</strong> — 招聘信息完整透明</div>
- <div>③ <strong style="color:var(--text-1);">内容审核到位</strong> — 虚假信息严格过滤</div>
- </div>
- <div style="font-size:0.85rem;color:var(--text-2);line-height:1.8;">
- <div style="margin-bottom:8px;">④ <strong style="color:var(--text-1);">风险防范到位</strong> — 建立风险预警机制</div>
- <div>⑤ <strong style="color:var(--text-1);">处置管控到位</strong> — 违规内容快速处置</div>
- </div>
- </div>
- </div>
- </div>
- </section>
- <!-- Ch8: 快聘4P营销策略 -->
- <section class="report-section" data-slide-num="08" data-tone="blue" id="ch8">
- <div class="section-inner">
- <div class="section-head">
- <div class="section-eyebrow" style="color:var(--blue);">CHAPTER 08</div>
- <h2 class="text-h1">快聘 4P 营销策略</h2>
- <p class="section-sub">产品 / 价格 / 渠道 / 促销</p>
- </div>
- ${fourPGrid()}
- ${insightBanner('08', '基于VOC的内容策略:围绕<strong>"确定性"</strong>构建传播矩阵', '最强购买点:工资真实、发薪稳定、包吃包住,当天入职。最强焦虑:被骗、被坑、黑中介、押金不退。内容方向应围绕"真实岗位展示+主播真实推荐+快速入职承诺"。', [
- { num: '20+', lbl: '情绪关键词体系' },
- { num: '3', lbl: '核心传播维度' },
- { num: '1个', lbl: '综艺IP标杆' },
- ])}
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--blue);margin-bottom:14px;font-weight:600;">📊 H1-H8 假设验证</div>
- ${hypothesisBarChart()}
- </div>
- </section>
- <!-- Ch9: VOC证据锚点 -->
- <section class="report-section" data-slide-num="09" data-tone="amber" id="ch9">
- <div class="section-inner">
- <div class="section-head">
- <div class="section-eyebrow">CHAPTER 09</div>
- <h2 class="text-h1">VOC 证据锚点</h2>
- <p class="section-sub">可直接引用的用户原声</p>
- </div>
- <div class="grid grid-2">
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--rose);margin-bottom:14px;font-weight:600;">🔥 薪资焦虑类高赞VOC</div>
- ${SALARY_VOC.map(v => voiceCard(v, false)).join('\n')}
- </div>
- <div>
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--rose);margin-bottom:14px;font-weight:600;">🔥 风险担忧类高赞VOC TOP10</div>
- ${HIGH_LIKED.map(v => voiceCard(v, v.hot)).join('\n')}
- </div>
- </div>
- <div class="card card-glow" style="border-left:3px solid var(--green);margin-top:28px;padding:28px 32px;">
- <div style="font-family:var(--font-mono);font-size:0.72rem;letter-spacing:0.15em;color:var(--green);margin-bottom:14px;font-weight:600;">💡 核心洞察</div>
- <div class="text-h3" style="margin-bottom:12px;">情绪价值的核心不是娱乐,而是"确定性"</div>
- <p class="text-body">快手在蓝领求职场景中的优势,来自内容触达、直播解释、即时互动和高频决策场景的天然匹配。需求高频、风险敏感、决策链短、转化诉求强。</p>
- </div>
- </div>
- </section>
- </main>
- <footer>
- <div>快手快聘 · VOC 深度洞察报告</div>
- <div class="meta">数据来源:TikHub API 快手评论采集 | VOC 7,902条 | 视频380条 | 19个关键词 | 播放量9,455万</div>
- <div class="meta">数据截至:2026年4月 | 报告版本:v7</div>
- </footer>
- <script>
- (function(){
- const slides=document.querySelectorAll('.report-section');
- const nav=document.getElementById('navRail');
- const now=document.getElementById('slideNow');
- const bar=document.getElementById('slideBar');
- const total=document.getElementById('slideTotal');
- const progress=document.getElementById('progressLine');
- const tips=['封面','执行摘要','快聘生态','心理洞察','薪资权益','风险焦虑','信任机制','机会建议','4P策略','VOC锚点'];
- let current=0;
- tips.forEach((t,i)=>{const d=document.createElement('div');d.className='nav-dot';d.setAttribute('data-tip',t);d.addEventListener('click',()=>slides[i]&&slides[i].scrollIntoView({behavior:'smooth'}));nav.appendChild(d);});
- const update=()=>{const idx=Math.round(window.scrollY/window.innerHeight);current=Math.max(0,Math.min(idx,slides.length-1));now.textContent=String(current+1).padStart(2,'0');bar.style.setProperty('--pct',((current+1)/slides.length*100)+'%');progress.style.width=((current+1)/slides.length*100)+'%';document.querySelectorAll('.nav-dot').forEach((d,i)=>{d.classList.toggle('active',i===current);});};
- window.addEventListener('scroll',update,{passive:true});
- document.addEventListener('keydown',e=>{if(e.key==='ArrowDown'||e.key==='PageDown'){e.preventDefault();slides[current+1]&&slides[current+1].scrollIntoView({behavior:'smooth'});}else if(e.key==='ArrowUp'||e.key==='PageUp'){e.preventDefault();slides[current-1]&&slides[current-1].scrollIntoView({behavior:'smooth'});}});
- document.querySelectorAll('.kbd-hint')[0]&&document.querySelectorAll('.kbd-hint')[0].classList.add('fade');
- window.addEventListener('scroll',()=>{document.querySelectorAll('.kbd-hint')[0]&&document.querySelectorAll('.kbd-hint')[0].classList.add('fade');},{passive:true});
- total.textContent=String(slides.length).padStart(2,'0');
- update();
- document.documentElement.classList.add('js');
- const observer=new IntersectionObserver(entries=>{entries.forEach(e=>{if(e.isIntersecting){e.target.classList.add('in-view');const fadeups=e.target.querySelectorAll('.fade-up');fadeups.forEach((el,i)=>setTimeout(()=>el.classList.add('is-visible'),i*100));}}});slides.forEach(s=>observer.observe(s));
- })();
- </script>
- </body>
- </html>`;
- fs.writeFileSync(OUT_FILE, html, 'utf-8');
- console.log('HTML report generated:', OUT_FILE);
- console.log('File size:', (fs.statSync(OUT_FILE).size / 1024).toFixed(1), 'KB');
|