/* 移动端主页 — 数据库为主体,Agent 买电视助手为入口,小工具页挂前台工具。 两个已上线工具(mobile.jsx 的 ComposeApp/LightApp)在壳内 push 打开,一个手机壳一站到底。 复用 icons.jsx(Icon) / data.jsx(TVS) / images.jsx(IMG)。深链:?tab=tools · ?tool=compose|light */ const { useState, useEffect, useRef } = React; const cx = (...a) => a.filter(Boolean).join(' '); const q = (k) => new URLSearchParams(location.search).get(k); const REDUCE = !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches); function Phone({ children }) { return (
{children}
); } const ASKS = ['预算 1.5 万,客厅怎么选', '白天客厅很亮,怕反光', 'OLED 会烧屏吗']; const CMD = '我想买台客厅用的电视'; // 演示固定命令:点对话框打字机输入,点发送进入 Agent 对话 /* 前台工具:2 个已上线(壳内打开),4 个规划位 */ const LIVE_TOOLS = [ { k: 'compose', icon: 'sparkle', n: 'AI 客厅合成', d: '拍下电视墙,AI 生成等比合成预览', img: () => IMG.wall }, { k: 'light', icon: 'sun', n: '采光 · 测光', d: '两拍测照度与反光源,反推抗反需求', img: () => IMG.rev }, ]; const PLAN_TOOLS = [ { icon: 'scan', n: 'AR 量房 · 摆放', d: '量电视墙净宽与入户路径', tag: '规划中' }, { icon: 'cube', n: '旧机估值变现', d: '拍照识别 → 含搬运费估价', tag: '规划中' }, { icon: 'eye', n: '开箱验机 · 故障上报', d: '拍照验收,反哺故障率库', tag: '规划中' }, { icon: 'crosshair', n: '交互对比看板', d: '并排对比实测画质与参数', tag: '网页版', href: '电视对比看板.html' }, ]; function HomeScreen({ on, openDb, openAgent, frozen }) { const [ask, setAsk] = useState(''); const [typing, setTyping] = useState(false); const tRef = useRef(null); useEffect(() => () => clearInterval(tRef.current), []); /* 点对话框:打字机敲入固定命令 */ const typeCmd = () => { if (typing || ask === CMD) return; clearInterval(tRef.current); setTyping(true); setAsk(''); let i = 0; tRef.current = setInterval(() => { i++; setAsk(CMD.slice(0, i)); if (i >= CMD.length) { clearInterval(tRef.current); setTyping(false); } }, REDUCE ? 4 : 52); }; /* 点发送:从 Agent 卡片 morph 进对话(空输入时直接用固定命令) */ const send = (e) => { if (typing) return; e.stopPropagation(); openAgent(e.currentTarget.closest('.agent'), ask || CMD); }; return (
先看
电视数据库
{/* Agent 买电视助手 — 主入口 */}
AI 买电视助手
从选到装到售后,直接问
{ask || (typing ? '' : '想买多大?预算多少?点我试试…')} {typing && }
{ASKS.map((a) => )}
{/* 数据资产 — 用户视角:在库量 + 即将入库 */}
数据资产
{DB_STATS.inStock} 台电视
实测在库
{DB_STATS.incoming} 台即将入库 {DB_STATS.brands} 个品牌
{/* 机型库 — 自动滚动条 + 全部入口 */}
机型库
{REDUCE ? (
{DBTV.map((t) => )}
) : (
{/* 壳内层打开时暂停跑马灯,省掉层下每帧重绘 */}
{DBTV.map((t) => )}
)}
); } function TvCard({ t, onClick }) { return (
{t.brand} {t.model}
{t.panel}{t.size}″
{t.hl}
¥{t.price.toLocaleString()}
); } /* DbScreen(详细机型库)在 dbview.jsx */ function ToolsScreen({ on, openTool }) { return (
小工具
{LIVE_TOOLS.map((t) => ( ))}
规划中
{PLAN_TOOLS.map((t) => { const inner = (
{t.n}{t.d}
{t.tag}
); return t.href ? {inner} :
{inner}
; })}
); } /* 壳内层:两段式 morph(防卡顿)。 ① 空面板从来源卡片 rect clip-path 张开(动画帧里没有内容挂载/重绘成本); ② 落定后再挂载内容淡入。关闭时内容先淡出、面板反向收回后卸载(fresh mount)。 rect 为 null(深链直开)时从屏幕中央小窗张开。 */ function MorphLayer({ rect, closing, children }) { const [on, setOn] = useState(false); const [ready, setReady] = useState(REDUCE); useEffect(() => { const r = requestAnimationFrame(() => setOn(true)); const t = setTimeout(() => setReady(true), REDUCE ? 0 : 460); return () => { cancelAnimationFrame(r); clearTimeout(t); }; }, []); const from = rect ? `inset(${rect.top}px ${rect.right}px ${rect.bottom}px ${rect.left}px round 22px)` : 'inset(42% 24% 42% 24% round 26px)'; const open = on && !closing; return (
{ready && children}
); } function HomeApp() { const tool0 = ['compose', 'light', 'db', 'agent'].includes(q('tool')) ? q('tool') : null; const [tab, setTab] = useState(tool0 === 'compose' || tool0 === 'light' || q('tab') === 'tools' ? 'tools' : 'home'); const [tool, setTool] = useState(tool0); const [agentAsk, setAgentAsk] = useState(CMD); const [rect, setRect] = useState(null); const [closing, setClosing] = useState(false); const syncUrl = (fn) => { const u = new URL(location); fn(u.searchParams); history.replaceState(null, '', u); }; const pick = (t) => { if (t === tab) return; syncUrl((p) => { if (t === 'tools') p.set('tab', 'tools'); else p.delete('tab'); }); setTab(t); }; /* 记录点击卡片相对手机屏的 rect(壳被缩放时按比例还原),morph 从这里张开 */ const openTool = (k, e) => { let r = null; const host = e && e.currentTarget.closest('.m-app'); if (host) { const hr = host.getBoundingClientRect(); const cr = e.currentTarget.getBoundingClientRect(); const s = hr.width ? host.offsetWidth / hr.width : 1; r = { top: (cr.top - hr.top) * s, left: (cr.left - hr.left) * s, right: (hr.right - cr.right) * s, bottom: (hr.bottom - cr.bottom) * s }; } setRect(r); syncUrl((p) => p.set('tool', k)); setClosing(false); setTool(k); }; const closeTool = () => { syncUrl((p) => p.delete('tool')); setClosing(true); setTimeout(() => { setTool(null); setClosing(false); }, REDUCE ? 60 : 650); }; return (
openTool('db', e)} openAgent={(el, text) => { setAgentAsk(text); openTool('agent', el && { currentTarget: el }); }} frozen={!!tool} /> {tool && ( {tool === 'db' ? : tool === 'agent' ? : tool === 'light' ? : } )}
{[['home', '首页', 'database'], ['tools', '小工具', 'grid']].map(([k, l, ic]) => ( ))}
); } function StageHome() { return (

电视数据库 · 移动端主页

数据库是主体,Agent 买电视助手是主入口;小工具页只放手机才能测的前台工具,两个已上线 demo 可直接点进。

Agent 主导 Liquid Glass 双 Tab
深链:?tab=tools 直达小工具 · ?tool=compose|light|db|agent 壳内直开工具 / 机型库 / 买电视助手
); } /* 真机模式:小屏(真手机/窄窗)全屏直跑,不套假手机壳;?m=1 强制真机、?m=0 强制评审舞台 */ const REAL = q('m') === '1' || (q('m') !== '0' && window.matchMedia('(max-width: 620px)').matches); if (REAL) document.documentElement.classList.add('real'); ReactDOM.createRoot(document.getElementById('root')).render( REAL ?
: );