/* 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 });