/* AI 买电视助手(壳内层)— 脚本化演示对话:固定命令 → 工具调用 → 选项式追问 → AR 量房回传 → 入户预检 → 结论机型卡(价格/链接)→ 旧机估值。 机型/价格取自 dbdata.jsx(BRANDS)与 data.jsx 实测值;对话为 demo 剧本,非真模型。 深链:?tool=agent · &auto=1 自动替用户点选项(走 AR→中档→备选→旧机全支线,评审/截图用) */ const AG_RM = !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches); const AG_AUTO = new URLSearchParams(location.search).get('auto') === '1'; const agCx = (...a) => a.filter(Boolean).join(' '); const AG_CMD = '我想买台客厅用的电视'; /* 尺寸 → 在库命中数 / 包装箱最长边(入户预检用) */ const AG_HIT = { 75: 8, 85: 21, 98: 11 }; const AG_BOX = { 75: { long: '1.86 m' }, 85: { long: '2.08 m' }, 98: { long: '2.36 m', warn: '箱体超 2.3 m:需斜立进梯,建议约 2 名搬运(免费)' }, }; /* 预算选项按尺寸给(价格全部来自机型库在售录价) */ const AG_BUDGETS = { 75: [ { label: '5 – 6 千', v: '75b1' }, { label: '1 万左右', v: '75b2' }, { label: '不确定,直接推荐', v: '75b2' }, ], 85: [ { label: '1 万以内', v: '85low' }, { label: '1 – 1.5 万', v: '85mid' }, { label: '不设上限', v: '85high' }, ], 98: [ { label: '3 万左右', v: '98b1' }, { label: '5 万以上', v: '98b2' }, { label: '不确定,直接推荐', v: '98b1' }, ], }; const AG_PICKS = { '75b1': { say: '5–6 千的 75″,这台生态和画质都最顺——', main: { brand: '华为', model: '智慧屏 Vision 5 75', panel: 'MiniLED', size: 75, zone: '392 区', nit: '1600 nit', refl: '3.0%', price: 5647, hl: '鸿蒙生态', why: '1600 nit 应对白天客厅够用,鸿蒙投屏/联动是同价位独一档;实测系统无广告。' }, altSay: '还想再省一千的话:', alt: { brand: '华为', model: 'Vision 5 SE 75', panel: 'LED', size: 75, zone: '直下背光', nit: '800 nit', refl: '3.4%', price: 4499, hl: '入门首选', why: '亮度低一档,晚上看为主完全够;预算敏感选它不亏。' }, }, '75b2': { say: '1 万左右的 75″,白天客厅我推这台——', main: { brand: '海信', model: '75 E7Q', panel: 'MiniLED', size: 75, zone: '1120 区', nit: '2600 nit', refl: '2.8%', price: 9749, hl: '亮度扎实', why: '2600 nit 峰值 + 低反屏,强环境光下高光不发灰;游戏 144Hz 顺带满足。' }, altSay: '想砍一半预算也有得选:', alt: { brand: '华为', model: '智慧屏 Vision 5 75', panel: 'MiniLED', size: 75, zone: '392 区', nit: '1600 nit', refl: '3.0%', price: 5647, hl: '鸿蒙生态', why: '亮度低一档但生态好用,混龄家庭很省心。' }, }, '85low': { say: '1 万内的 85″,这台几乎没有对手——', main: { brand: '雷鸟', model: '85 鹤7 PRO 25款', panel: 'QD-MiniLED', size: 85, zone: '1680 区', nit: '3200 nit', refl: '2.6%', price: 9799, hl: '同价位控光标杆', why: '万元内把亮度和分区堆到顶,白天客厅高光场景不发灰;144Hz 高刷打游戏也顺带满足。' }, altSay: '同档还有台更均衡的备选:', alt: { brand: '海信', model: '85 E7N PRO', panel: 'MiniLED', size: 85, zone: '1120 区', nit: '2400 nit', refl: '2.8%', price: 9999, hl: '均衡水桶机', why: '亮度略低半档,但色彩调校更稳、系统更省心,长辈混用更友好。' }, }, '85mid': { say: '1–1.5 万这档,白天亮客厅我只推它——', main: { brand: '海信', model: '85 E8S', panel: 'RGB-MiniLED', size: 85, zone: '2340 区', nit: '5000 nit', refl: '2.4%', price: 14999, hl: 'RGB 光晕控制出色', why: '峰值 5000 nit + RGB 三色控光,你家这种强环境光客厅正是它的主场;实测反射率 2.4% 是同价位最低。' }, altSay: '想省 3 千的话,看这台:', alt: { brand: 'TCL', model: '85 Q10L', panel: 'QD-MiniLED', size: 85, zone: '万象分区', nit: '4500 nit', refl: '2.8%', price: 11999, hl: '超窄金属边', why: '亮度同样管够,光晕控制略逊 E8S,但直接便宜 3000 元。' }, }, '85high': { say: '不设上限的话,客厅直接一步到位——', main: { brand: '海信', model: '100 E8Q PRO', panel: 'RGB-MiniLED', size: 100, zone: '3000+ 区', nit: '5000 nit', refl: '2.4%', price: 28999, hl: '百吋影院级', why: '既然墙放得下,100″ 沉浸感是 85″ 追不上的;白天抗反、夜里控光都是这档天花板。' }, altSay: '想要挂墙更像一幅画的话:', alt: { brand: '三星', model: '85LS03D', panel: 'QLED', size: 85, zone: '哑光屏', nit: '2000 nit', refl: '1.9%', price: 23999, hl: 'The Frame 画壁', why: '哑光屏几乎零反光 + 艺术画模式,客厅颜值党的首选。' }, }, '98b1': { say: '3 万左右的百吋档,这两台打架,先看它——', main: { brand: '海信', model: '100 E8Q PRO', panel: 'RGB-MiniLED', size: 100, zone: '3000+ 区', nit: '5000 nit', refl: '2.4%', price: 28999, hl: 'RGB 控光', why: '100″ + 5000 nit,白天强光客厅唯一不虚的百吋;光晕控制实测比同档好半档。' }, altSay: '备选是隔壁这台:', alt: { brand: 'TCL', model: '98 Q10L PRO', panel: 'QD-MiniLED', size: 98, zone: '万象分区', nit: '4800 nit', refl: '2.7%', price: 28999, hl: '音响更强', why: '同价同亮度,安桥音响低频更猛,看演唱会/大片更爽。' }, }, '98b2': { say: '5 万以上就是旗舰随便挑了——', main: { brand: '海信', model: '98 UX', panel: 'RGB-MiniLED', size: 98, zone: '10000+ 区', nit: '8000 nit', refl: '2.2%', price: 51999, hl: '灯塔级旗舰', why: '万级分区 + 8000 nit,目前在库实测天花板;HDR 高光是别家两倍的观感。' }, altSay: '理性一点的备选:', alt: { brand: '海信', model: '100 E8Q PRO', panel: 'RGB-MiniLED', size: 100, zone: '3000+ 区', nit: '5000 nit', refl: '2.4%', price: 28999, hl: '性价比旗舰', why: '尺寸还大 2″,价格差出一台 85″——追求面积优先选它。' }, }, }; const agSleep = (ms) => new Promise((r) => setTimeout(r, AG_RM ? Math.min(ms, 80) : ms)); /* ---- 假 AR 量房浮层:识别平面 → 量净宽 → 量视距 → 回传 ---- */ function AgArOverlay({ onDone }) { const { useState, useEffect } = React; const [ph, setPh] = useState(0); useEffect(() => { const k = AG_RM ? 0.1 : 1; const ts = [ setTimeout(() => setPh(1), 1000 * k), setTimeout(() => setPh(2), 2300 * k), setTimeout(() => setPh(3), 3600 * k), setTimeout(onDone, 4500 * k), ]; return () => ts.forEach(clearTimeout); }, []); const hints = ['正在识别电视墙平面…', '测量电视墙净宽…', '测量沙发观看距离…', '测量完成,回传助手']; return (
{hints[ph]}
{ph >= 1 &&
净宽 3.62 m
} {ph >= 2 &&
视距 2.9 m
}
= 3 && 'ok')}> {ph >= 3 ? : } {ph >= 3 ? '数据已回传买电视助手' : 'AR 量房小工具运行中'}
); } /* ---- 消息卡片 ---- */ function AgTvCard({ tv, tag }) { const color = (BRANDS.find((b) => b.zh === tv.brand) || {}).color || 'var(--accent)'; const jd = 'https://search.jd.com/Search?keyword=' + encodeURIComponent(`${tv.brand}${tv.model.replace(/\s+/g, '')}电视`); return (
{tv.brand} {tv.model} {tag}
{tv.panel}{tv.size}″{tv.hl}
峰值亮度{tv.nit}
实测反射率{tv.refl}
控光{tv.zone}

{tv.why}

¥{tv.price.toLocaleString()}今日含国补到手
去京东 加入对比
); } function AgEntryCard({ size }) { const box = AG_BOX[size] || AG_BOX[85]; return (
入户可行性预检通过 ✓
包装箱最长边 {box.long}电梯轿厢 1.6×1.5×2.3 m
入户门净宽 0.95 m拆箱前可直进
楼道转角 · 无台阶路径已核对
{box.warn &&
{box.warn}
}
数据来自小区众包入户库(演示数据)
); } function AgArCard() { return (
AR 量房 · 数据回传
电视墙净宽3.62 m
沙发视距2.9 m
推荐尺寸85″
); } function AgTradeCard() { return (
旧机估值 · 回收抵扣
¥360 – 52055″ 液晶 · 约 2017 年 · 屏幕完好
含上门拆挂与搬运费 · 回收款下单直接抵扣
); } function AgEndCard({ size, bud, name, traded }) { return (
本次方案小结
{size}″{bud}{name} 送装 + 挂墙{traded && 旧机抵扣 ✓}
); } /* ---- 聊天主体:剧本引擎 ---- */ function AgentChatInner({ onBack, onRestart, ask }) { const { useState, useEffect, useRef } = React; const [msgs, setMsgs] = useState([]); const [ar, setAr] = useState(false); const seq = useRef(0); const alive = useRef(true); const pend = useRef(null); // 选项等待 {id, res} const arRes = useRef(null); // AR 等待 resolve const scRef = useRef(null); useEffect(() => () => { alive.current = false; }, []); useEffect(() => { const el = scRef.current; if (el) el.scrollTo({ top: el.scrollHeight, behavior: AG_RM ? 'auto' : 'smooth' }); }, [msgs, ar]); const push = (m) => { const id = ++seq.current; setMsgs((s) => [...s, { id, ...m }]); return id; }; const patch = (id, p) => setMsgs((s) => s.map((m) => (m.id === id ? { ...m, ...p } : m))); const sleep = async (ms) => { await agSleep(ms); if (!alive.current) throw 0; }; const ai = async (text, wait = 950) => { const id = push({ kind: 'ai', typing: true }); await sleep(wait); patch(id, { typing: false, text }); await sleep(260); }; const tool = async (icon, name, detail, result, wait = 1400) => { const id = push({ kind: 'tool', icon, name, detail, state: 'run' }); await sleep(wait); patch(id, { state: 'done', result }); await sleep(320); }; const opts = (items) => new Promise((res) => { const id = push({ kind: 'opts', items }); pend.current = { id, res }; if (AG_AUTO) agSleep(1100).then(() => { if (pend.current && pend.current.id === id) pick(items.find((it) => it.wide) || items[Math.min(1, items.length - 1)]); }); }); const pick = (it) => { const p = pend.current; if (!p) return; pend.current = null; patch(p.id, { done: true }); push({ kind: 'user', text: it.bubble || it.label }); p.res(it); }; const arRun = () => new Promise((res) => { arRes.current = res; setAr(true); }); const arDone = () => { setAr(false); const r = arRes.current; arRes.current = null; if (r) r(); }; useEffect(() => { run().catch(() => {}); }, []); async function run() { push({ kind: 'user', text: ask || AG_CMD }); await sleep(650); await tool('database', '检索机型库', '按「客厅」场景匹配在库实测机', '在库 58 台 · 客厅适配 32 台'); await tool('sliders', '加载场景权重', '客厅 · 强环境光', '亮度 30% + 抗反光 25%,权重就位', 1100); await ai('收到!客厅白天环境光强,我会重点比「亮度」和「抗反光」两项实测。先定尺寸——你倾向多大?'); const s = await opts([ { label: '75″', sub: '视距 2.3 – 2.8 m', v: 75 }, { label: '85″', sub: '视距 2.8 – 3.4 m', v: 85 }, { label: '98″ 以上', sub: '视距 3.4 m+', v: 98 }, { label: '不知道怎么选,帮我量一下', sub: '跳转 AR 量房小工具 · 10 秒自动回传', v: 'ar', accent: true, wide: true, bubble: '不知道怎么选' }, ]); let size = s.v; if (size === 'ar') { await ai('没问题,我拉起 AR 量房,镜头对着电视墙慢慢扫一圈就行。', 750); await sleep(900); await arRun(); push({ kind: 'ar' }); await sleep(750); await ai('数据回来了:净宽 3.62 m、视距 2.9 m。按 THX 40° 视角算,85″ 正好在甜点区;98″ 放得下但视距偏近。我按 85″ 帮你找。'); size = 85; } await tool('cube', '入户可行性预检', `${size}″ 包装箱 × 小区电梯库`, '电梯与入户门均可通过 ✓', 1600); push({ kind: 'entry', size }); await sleep(750); await ai('入户没问题 ✓ 再确认下预算,大概什么范围?'); const b = await opts(AG_BUDGETS[size]); const P = AG_PICKS[b.v]; await tool('search', '筛选在库机型', `${size}″ · 客厅高亮 · ${b.label}`, `58 台 → 命中 ${AG_HIT[size]} 台 → 初选 3 台`); await tool('activity', '拉取实测数据', '峰值/全屏亮度 · 反射率 · 控光分区', '3 台整档实测齐全,可横向对比', 1500); await tool('bolt', '全网比价', '今日到手价 · 含国补', '已按最低到手价排序', 1150); await ai(P.say); push({ kind: 'tv', tv: P.main, tag: '首选' }); await sleep(750); const fb = await opts([ { label: '很合适,就它了', v: 'ok', accent: true }, { label: '再看看备选', v: 'alt' }, ]); if (fb.v === 'alt') { await ai(P.altSay, 700); push({ kind: 'tv', tv: P.alt, tag: '备选' }); await sleep(750); await ai('两台链接都在上面了,价格实时同步,随时能下单。', 650); } else { await ai('好眼光,这台也是我在这档的第一推荐 👌', 650); } await ai('对了,家里旧电视要一起处理吗?拍两张照就能出回收价,能直接抵新机。'); const tr = await opts([ { label: '要,帮我估个价', v: 'y', accent: true }, { label: '不用了', v: 'n' }, ]); let traded = false; if (tr.v === 'y') { await tool('camera', '旧机估值', '型号识别 · 成色问答 · 含上门搬运', '识别:55″ 液晶(约 2017 年)', 1700); push({ kind: 'trade' }); traded = true; await sleep(750); } await ai('搞定!方案小结在下面:链接直达下单页,送装、挂墙和旧机回收可以约同一天上门。有新问题随时回来找我 ✌️'); push({ kind: 'end', size, bud: b.label, name: `${P.main.brand} ${P.main.model}`, traded }); } return (
AI 买电视助手已接入 {DB_STATS.inStock} 台实测机型库
{msgs.map((m) => { switch (m.kind) { case 'user': return
{m.text}
; case 'ai': return (
{m.typing ? : m.text}
); case 'tool': return (
{m.name} {m.state === 'done' ? m.result : m.detail}
{m.state === 'run' ? : }
); case 'opts': return m.done ? null : (
{m.items.map((it, i) => ( ))}
); case 'ar': return ; case 'entry': return ; case 'tv': return ; case 'trade': return ; case 'end': return ; default: return null; } })}
选择上方选项继续(演示为固定剧本)
{ar && }
); } /* 外壳:epoch 换 key 实现「重新演示」整段重跑 */ function AgentChat(props) { const { useState } = React; const [ep, setEp] = useState(0); return setEp((e) => e + 1)} />; } Object.assign(window, { AgentChat });