| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
- <title>WorkBuddy 移动端 UI 复刻 · 交互演示</title>
- <script src="https://cdn.tailwindcss.com/3.4.16"></script>
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.7.2/css/all.min.css">
- <script>
- tailwind.config = {
- theme: {
- extend: {
- colors: {
- soft: '#f5f5f7', // 次级底色 / 胶囊轨道 / 三方登录圆底
- fill: '#ececee', // 用户气泡
- line: '#ebebef', // 分割线
- ink: '#1a1a1a', // 主文字
- ink2: '#4a4a4f',
- muted: '#8e8e93', // 次级文字
- faint: '#b8b8bd', // 占位符 / 折叠步骤文字
- brand: '#17b58b', // 品牌薄荷绿
- brandsoft: '#e6f7f1',
- reasoning: '#5b6cf0', // “推理”标签蓝
- vision: '#3fae72', // “视觉”标签绿
- },
- fontFamily: {
- sans: ['"PingFang SC"', '"HarmonyOS Sans SC"', '"Noto Sans SC"', 'system-ui', '-apple-system', 'sans-serif'],
- },
- boxShadow: {
- float: '0 6px 18px -8px rgba(0,0,0,.18), 0 1px 2px rgba(0,0,0,.04)',
- dock: '0 10px 40px -12px rgba(0,0,0,.16)',
- sheet: '0 -12px 40px -20px rgba(0,0,0,.25)',
- },
- keyframes: {
- pop: { '0%': { opacity: 0, transform: 'translateY(10px) scale(.98)' }, '100%': { opacity: 1, transform: 'none' } },
- up: { '0%': { transform: 'translateY(100%)' }, '100%': { transform: 'none' } },
- },
- animation: { pop: 'pop .28s cubic-bezier(.2,0,0,1) both', up: 'up .32s cubic-bezier(.2,0,0,1) both' },
- },
- },
- };
- </script>
- <style>
- html, body { background: #e9e9ec; }
- body { font-family: "PingFang SC","HarmonyOS Sans SC","Noto Sans SC",system-ui,-apple-system,sans-serif; -webkit-font-smoothing: antialiased; }
- /* 截图基准 1170×2532@3x → 390×844 容器 */
- .phone { width: 390px; height: 844px; border-radius: 44px; overflow: hidden; position: relative; background: #fff;
- box-shadow: 0 30px 60px -20px rgba(0,0,0,.35), 0 0 0 10px #111, 0 0 0 11px #333; }
- .screen { position: absolute; inset: 0; display: none; flex-direction: column; background: #fff; }
- .screen.on { display: flex; }
- .hide { display: none !important; }
- /* 吉祥物素材源自白底截图:用 multiply 叠到任意底色上,去掉白色方块 */
- .blend { mix-blend-mode: multiply; }
- ::-webkit-scrollbar { width: 0; height: 0; }
- @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
- </style>
- </head>
- <body class="min-h-screen text-ink">
- <header class="max-w-[1500px] mx-auto px-8 pt-10 pb-6 flex items-center gap-4">
- <img src="assets/app-logo.png" class="w-11 h-11 rounded-xl" alt="WorkBuddy">
- <div class="flex-1">
- <h1 class="text-2xl font-semibold">WorkBuddy 移动端 UI 复刻</h1>
- <p class="text-sm text-ink2/70 mt-1">腾讯云 AI 工作台 · 依据 uinotes.com 收录截图还原 · HTML + Tailwind CSS 3 + Font Awesome 6</p>
- </div>
- <div class="flex gap-2 text-sm" id="filters">
- <button onclick="filter('all')" data-f="all" class="px-4 py-2 rounded-full">全部</button>
- <button onclick="filter('basic')" data-f="basic" class="px-4 py-2 rounded-full">基础版</button>
- <button onclick="filter('full')" data-f="full" class="px-4 py-2 rounded-full">完整版</button>
- </div>
- </header>
- <main id="wall" class="max-w-[1500px] mx-auto px-8 pb-20 flex flex-wrap gap-12 items-start justify-center"></main>
- <template id="tpl-phone">
- <div class="phone">
- <div class="h-[54px] shrink-0 flex items-center justify-between px-8 pt-1.5 text-[15px] font-semibold relative z-30">
- <span>03:25</span>
- <div class="absolute left-1/2 -translate-x-1/2 top-[6px] bg-[#141414] text-white text-[10px] rounded-full px-3 py-[3px] flex items-center gap-1">
- <i class="fa-brands fa-unsplash text-[9px]"></i> uinotes.com
- </div>
- <span class="flex items-center gap-1.5 text-[12px]">
- <i class="fa-solid fa-signal"></i><i class="fa-solid fa-wifi"></i>
- <span class="w-7 h-[13px] rounded-[3px] border border-black/80 relative"><span class="absolute inset-[2px] right-[9px] bg-black rounded-[1px]"></span></span>
- </span>
- </div>
- <!-- 01 引导首页 -->
- <section class="screen on" data-screen="intro">
- <div class="flex-1 relative pt-14">
- <button class="absolute left-4 top-1 w-10 h-10 rounded-full bg-white shadow-float flex items-center justify-center z-20" onclick="openSideIn(this)"><i class="fa-solid fa-bars-staggered"></i></button>
- <div class="absolute inset-x-0 top-[40%] -translate-y-1/2 flex flex-col items-center">
- <img src="assets/mascot.png" class="w-[190px] blend bg-[#f4f4f6] rounded-xl" alt="">
- <h2 class="mt-3 text-[30px] font-bold">Workbuddy 我帮你</h2>
- <div class="mt-5 p-1 rounded-full bg-soft flex text-[16px] w-[252px]">
- <button class="flex-1 py-2.5 rounded-full bg-white shadow-sm font-medium">云端工作</button>
- <button class="flex-1 py-2.5 rounded-full text-muted">连接电脑</button>
- </div>
- </div>
- <div class="absolute left-4 right-4 bottom-4">
- <div class="flex gap-2 text-[15px] mb-3">
- <button class="flex-1 h-11 rounded-xl border border-line flex items-center justify-center gap-1.5"><i class="fa-regular fa-images"></i>幻灯片</button>
- <button class="flex-1 h-11 rounded-xl border border-line flex items-center justify-center gap-1.5"><i class="fa-regular fa-circle-play"></i>视频生成</button>
- <button class="flex-1 h-11 rounded-xl border border-line flex items-center justify-center gap-1.5 text-[14px]"><i class="fa-solid fa-magnifying-glass-arrow-right text-[12px]"></i>深度研究</button>
- <button class="w-[74px] h-11 rounded-xl border border-line">更多</button>
- </div>
- <div class="rounded-[26px] bg-white shadow-dock p-4">
- <div class="h-7 text-[17px] text-faint">发消息...</div>
- <div class="flex items-center mt-2 gap-3">
- <i class="fa-solid fa-signal text-[15px] -rotate-45"></i>
- <button class="flex items-center gap-1 text-[15px]" onclick="openModelsIn(this)">选择模型 <i class="fa-solid fa-chevron-down text-[11px] text-faint"></i></button>
- <button class="ml-auto w-9 h-9 rounded-full flex items-center justify-center" onclick="toast(this, '添加附件 / 文件')"><i class="fa-solid fa-plus text-lg"></i></button>
- </div>
- </div>
- </div>
- </div>
- </section>
- <!-- 02 登录页 -->
- <section class="screen" data-screen="login">
- <div class="flex-1 flex flex-col">
- <div class="mt-[22%] flex flex-col items-center">
- <img src="assets/mascot.png" class="w-[196px]" alt="">
- <h2 class="mt-3 text-[28px] font-medium">WorkBuddy,我帮你</h2>
- <p class="mt-2 text-[17px] text-[#b3b3b8]">深度研究</p>
- </div>
- <div class="mt-auto px-6 pb-6">
- <button class="w-full h-[56px] rounded-2xl bg-[#1c1c1e] text-white text-[18px] font-medium" onclick="toast(this, '验证码已发送')">手机号登录</button>
- <div class="flex justify-between px-6 mt-7">
- <button class="flex flex-col items-center gap-2"><span class="w-14 h-14 rounded-full bg-soft flex items-center justify-center text-[22px]"><i class="fa-brands fa-weixin text-[#1aad19]"></i></span><span class="text-[13px]">微信</span></button>
- <button class="flex flex-col items-center gap-2"><span class="w-14 h-14 rounded-full bg-soft flex items-center justify-center text-[22px]"><i class="fa-brands fa-qq"></i></span><span class="text-[13px]">QQ</span></button>
- <button class="flex flex-col items-center gap-2"><span class="w-14 h-14 rounded-full bg-soft flex items-center justify-center text-[20px]"><i class="fa-brands fa-dyalog"></i></span><span class="text-[13px]">企业版</span></button>
- <button class="flex flex-col items-center gap-2" onclick="openMoreIn(this)"><span class="w-14 h-14 rounded-full bg-soft flex items-center justify-center text-[20px]"><i class="fa-solid fa-ellipsis"></i></span><span class="text-[13px]">其他</span></button>
- </div>
- <label class="flex items-center justify-center gap-2 mt-6 text-[13px] text-ink2">
- <span class="w-[18px] h-[18px] rounded-full border-[1.5px] border-ink"></span>
- 已阅读并同意<span class="underline">服务协议</span>和<span class="underline">隐私政策</span>
- </label>
- </div>
- </div>
- </section>
- <!-- 03 新对话(登录落地态) -->
- <section class="screen" data-screen="home">
- <div class="flex-1 flex flex-col relative pt-14">
- <button class="absolute left-4 top-1 w-10 h-10 rounded-full bg-white shadow-float flex items-center justify-center z-20" onclick="openSideIn(this)"><i class="fa-solid fa-bars-staggered"></i></button>
- <div class="flex-1 flex flex-col items-center justify-center">
- <h2 class="text-[30px] font-bold">Workbuddy 我帮你</h2>
- <div class="mt-5 p-1 rounded-full bg-soft flex text-[16px] w-[252px]">
- <button class="flex-1 py-2.5 rounded-full bg-white shadow-sm font-medium">云端工作</button>
- <button class="flex-1 py-2.5 rounded-full text-muted">连接电脑</button>
- </div>
- <div class="mt-3 flex items-center gap-1.5 text-[13px] text-ink2"><i class="fa-regular fa-clock text-[11px]"></i>定时任务<i class="fa-solid fa-xmark text-[10px] text-muted ml-1"></i></div>
- </div>
- <div class="px-4 pb-4">
- <div class="rounded-[26px] bg-white shadow-dock p-4">
- <textarea rows="1" class="w-full resize-none outline-none text-[17px] placeholder:text-faint" placeholder="发消息..." oninput="composerInput(this)"></textarea>
- <div class="flex items-center mt-2 gap-3">
- <i class="fa-solid fa-signal text-[15px] -rotate-45"></i>
- <button class="flex items-center gap-1.5 text-[15px]" onclick="openModelsIn(this)"><span data-model-icon></span><span data-model-name>选择模型</span><i class="fa-solid fa-chevron-down text-[11px] text-faint"></i></button>
- <button class="ml-auto w-9 h-9 rounded-full flex items-center justify-center" onclick="toast(this, '添加附件 / 文件')"><i class="fa-solid fa-plus text-lg"></i></button>
- <button data-send class="w-9 h-9 rounded-full bg-[#1c1c1e] text-white flex items-center justify-center hide" onclick="enterChat(this, '运动推荐')"><i class="fa-solid fa-arrow-up text-[15px]"></i></button>
- </div>
- </div>
- </div>
- </div>
- </section>
- <!-- 07~10 任务对话(由脚本按页面填充 #chat-scroll) -->
- <section class="screen" data-screen="chat">
- <div class="h-[70px] shrink-0 flex items-end pb-2.5 px-4 gap-3">
- <button class="w-11 h-11 rounded-full bg-white shadow-float flex items-center justify-center" onclick="openSideIn(this)"><i class="fa-solid fa-bars-staggered"></i></button>
- <div class="flex-1 text-center text-[18px] font-medium truncate pr-10" data-chat-title>运动推荐</div>
- <button class="w-10 h-10 shrink-0 rounded-full bg-white shadow-float flex items-center justify-center" onclick="toast(this, '新对话')"><i class="fa-regular fa-square-plus text-[18px]"></i></button>
- </div>
- <div class="flex-1 overflow-y-auto px-4 pt-2 pb-4" data-chat-scroll></div>
- <div class="px-4 pb-4" data-composer>
- <div class="rounded-[26px] bg-white shadow-dock p-4">
- <textarea rows="1" class="w-full resize-none outline-none text-[17px] placeholder:text-faint" placeholder="发消息或按住说话"></textarea>
- <div class="flex items-center mt-2 gap-3">
- <i class="fa-solid fa-signal text-[15px] -rotate-45"></i>
- <button class="flex items-center gap-1.5 text-[15px]" onclick="openModelsIn(this)"><img src="assets/model-kimi.png" class="w-5 h-5 rounded-md" alt=""><span class="font-medium">Kimi-K3</span><i class="fa-solid fa-chevron-down text-[11px] text-faint ml-0.5"></i></button>
- <button class="ml-auto w-9 h-9 rounded-full flex items-center justify-center" onclick="toast(this, '添加附件 / 文件')"><i class="fa-solid fa-plus text-lg"></i></button>
- </div>
- </div>
- </div>
- </section>
- <div class="pointer-events-none absolute bottom-2 left-1/2 -translate-x-1/2 w-[134px] h-[5px] rounded-full bg-black z-[70]"></div>
- <!-- 侧栏 -->
- <div class="absolute inset-0 z-30 hide" data-layer="side">
- <div class="absolute inset-0 bg-black/5" onclick="closeSideIn(this)"></div>
- <aside class="absolute inset-y-0 left-0 w-[292px] bg-white shadow-[20px_0_40px_-20px_rgba(0,0,0,.25)] rounded-r-[26px] px-5 pt-[60px] pb-6 flex flex-col animate-pop">
- <div class="flex justify-end -mt-1 mb-3"><span class="w-10 h-10 rounded-full bg-soft flex items-center justify-center"><i class="fa-brands fa-codepen"></i></span></div>
- <div class="flex items-center gap-2 px-1.5">
- <span class="w-8 h-8 rounded-full bg-brandsoft text-brand flex items-center justify-center text-[13px] font-semibold">T</span>
- <span class="text-[16px]">Tony</span>
- </div>
- <div class="mt-4 p-1 rounded-full bg-soft flex text-[15px]">
- <button class="flex-1 py-2 rounded-full bg-white shadow-sm font-medium">云端工作</button>
- <button class="flex-1 py-2 rounded-full text-muted">连接电脑</button>
- </div>
- <nav class="mt-6 flex flex-col text-[17px]">
- <button class="flex items-center gap-3 px-1.5 py-3.5 text-left" onclick="closeSideIn(this)"><i class="fa-regular fa-clock text-[18px] w-5"></i>自动化<i class="fa-solid fa-chevron-right ml-auto text-[12px] text-faint"></i></button>
- <button class="flex items-center gap-3 px-1.5 py-3.5 text-left" onclick="closeSideIn(this)"><i class="fa-solid fa-diagram-project text-[16px] w-5"></i>项目<i class="fa-solid fa-chevron-right ml-auto text-[12px] text-faint"></i></button>
- <button class="flex items-center gap-3 px-1.5 py-3.5 text-left" onclick="closeSideIn(this)"><i class="fa-solid fa-user-astronaut text-[17px] w-5"></i>专家<i class="fa-solid fa-chevron-right ml-auto text-[12px] text-faint"></i></button>
- </nav>
- <div class="border-t border-line my-3"></div>
- <div class="flex items-center justify-between text-[15px] text-muted px-1.5"><span>任务</span><button class="text-brand">编辑</button></div>
- <button class="flex items-center gap-2 text-[17px] mt-4 px-1.5"><i class="fa-solid fa-plus"></i><b>新建任务</b></button>
- <div class="mt-1 flex flex-col">
- <button class="text-left px-3 py-3 rounded-lg bg-[#f4f4f6] text-[16px] flex items-center justify-between" onclick="openChat(this, '生成AI发展历程PPT')">生成AI发展历程PPT <i class="fa-solid fa-circle-notch fa-spin text-brand text-[14px]"></i></button>
- <button class="text-left px-3 py-3 text-[16px]" onclick="openChat(this, '收集今日AI资讯')">收集今日AI资讯</button>
- <button class="text-left px-3 py-3 text-[16px]" onclick="openChat(this, '运动推荐')">运动推荐</button>
- </div>
- </aside>
- </div>
- <!-- 选择模型 -->
- <div class="absolute inset-0 z-40 hide" data-layer="models">
- <div class="absolute inset-0 bg-black/25" onclick="closeModelsIn(this)"></div>
- <div class="absolute left-0 right-0 bottom-0 animate-up">
- <img src="assets/mascot-head.png" class="w-[168px] mx-auto -mb-1 relative z-10 blend" alt="">
- <div class="relative bg-white rounded-t-[26px] shadow-sheet pt-2 pb-8 px-6">
- <div class="w-10 h-1 rounded-full bg-[#e5e5e7] mx-auto"></div>
- <h3 class="text-center text-[19px] font-semibold mt-3 mb-3">选择模型</h3>
- <div data-model-list class="divide-y divide-line"></div>
- </div>
- </div>
- </div>
- <!-- 其他登录方式 -->
- <div class="absolute inset-0 z-40 hide" data-layer="more">
- <div class="absolute inset-0 bg-black/25" onclick="closeMoreIn(this)"></div>
- <div class="absolute inset-x-3 bottom-9 animate-up flex flex-col gap-2">
- <div class="bg-white rounded-2xl overflow-hidden text-[17px] divide-y divide-line">
- <button class="w-full py-4" onclick="toast(this, '其他登录')">其他登录</button>
- <button class="w-full py-4" onclick="toast(this, '扫码登录')">扫码登录</button>
- <button class="w-full py-4" onclick="closeMoreIn(this);toast(this, '已选择免登录使用')">免登录使用</button>
- </div>
- <button class="w-full bg-white rounded-2xl py-4 text-[17px]" onclick="closeMoreIn(this)">取消</button>
- </div>
- </div>
- </div>
- </template>
- <script>
- /* ============ 数据(对齐 uinotes 截图 #025 模型面板、#052 侧栏、#054 对话) ============ */
- const MODELS = [
- { icon: 'auto', name: 'Auto', tags: ['推理', '视觉'] },
- { icon: 'assets/model-hy3.png', name: 'Hy3', tags: ['推理', '视觉'] },
- { icon: 'assets/model-glm.png', name: 'GLM-5.2', tags: ['推理', '视觉'] },
- { icon: 'assets/model-glm.png', name: 'GLM-5.1', tags: ['推理'] },
- { icon: 'assets/model-glm.png', name: 'GLM-5v-Turbo', tags: ['推理', '视觉'] },
- { icon: 'assets/model-kimi.png', name: 'Kimi-K3', tags: ['推理', '视觉'] },
- { icon: 'assets/model-kimi.png', name: 'Kimi-K2.7-Code', tags: ['推理', '视觉'] },
- { icon: 'assets/model-kimi.png', name: 'Kimi-K2.6', tags: ['推理', '视觉'] },
- ];
- const PAGES = [
- { title: '01 · 引导首页', tag: 'basic', screen: 'intro', desc: '品牌主视觉、云端/本地切换、快捷能力胶囊、大输入框' },
- { title: '02 · 登录页', tag: 'basic', screen: 'login', desc: '吉祥物 + 手机号登录 / 三方登录 / 协议勾选' },
- { title: '03 · 新对话', tag: 'basic', screen: 'home', desc: '登录落地态:定时任务快捷胶囊、有输入时出现发送按钮' },
- { title: '04 · 选择模型', tag: 'basic', custom: 'models', desc: '底部面板,猫头从面板上沿探出;推理/视觉标签 + 勾选态' },
- { title: '05 · 其他登录方式', tag: 'basic', custom: 'more', desc: 'iOS 风格操作表:其他登录 / 扫码登录 / 免登录使用' },
- { title: '06 · 侧栏任务列表', tag: 'full', screen: 'home', open: ['side'], desc: '用户信息、功能入口、历史任务(进行中任务带 spinner)' },
- { title: '07 · 任务对话', tag: 'full', screen: 'chat', custom: 'preview', desc: '用户气泡、过程分段折叠、参数表格、AI 操作预览' },
- { title: '08 · 执行中 / 追问', tag: 'full', screen: 'chat', custom: 'typing', desc: '已折叠过程 + 思考中 + 编号选项追问卡片' },
- { title: '09 · 完成态', tag: 'full', screen: 'chat', custom: 'done', desc: '任务创建成功表格、💡 提示块、朗读/复制/分享操作栏' },
- { title: '10 · 选项式追问', tag: 'full', screen: 'chat', custom: 'ask', desc: '2/2 分页追问:单选编号选项 + 自定义输入' },
- ];
- /* ============ 片段 ============ */
- const USER_BUBBLE = (text = '收集今日ai资讯') => `
- <div class="flex justify-end mb-3">
- <div class="max-w-[78%] bg-fill rounded-[20px] rounded-tr-md px-4 py-3 text-[16px]">
- <span class="inline-flex items-center gap-1.5 bg-white rounded-full px-3 py-1 text-[13px] mb-2"><i class="fa-regular fa-clock text-[11px]"></i>定时任务</span>
- <div>${text}</div>
- </div>
- </div>`;
- const STEP = status => `
- <div class="mt-2 rounded-lg bg-[#f6f8f7] px-3 py-3 flex items-center justify-between text-[15px]">
- <span class="text-faint">拆解为 1 个步骤</span>
- <span class="${status === 'done' ? 'text-[#7cc3a8]' : 'text-muted'}">${status === 'done' ? '已完成' : '执行中'} <i class="fa-solid fa-chevron-right text-[10px]"></i></span>
- </div>`;
- const DONE_ROW = `
- <div class="flex items-center gap-2 text-[14px] text-muted py-1 mt-2"><span class="w-[7px] h-[7px] rounded-full bg-brand"></span>已完成</div>
- <div class="border-t border-line"></div>`;
- const FOLD = (n, openText) => `
- <div class="flex items-center justify-between text-[14px] text-muted mt-3">
- <span class="flex items-center gap-2"><span class="w-[7px] h-[7px] rounded-full bg-[#8fcfb9]"></span>${n}</span>
- <button class="border border-[#bfe6d8] rounded-full px-3 py-[3px] text-[13px] text-brand">${openText}</button>
- </div>`;
- const TABLE = (head, rows) => `
- <div class="mt-2 rounded-[10px] overflow-hidden border border-line">
- <table class="w-full text-[15px] table-fixed">
- <thead><tr class="bg-[#f2f6f9] text-left">
- <th class="px-3 py-2.5 font-semibold w-[92px] border-r border-white">${head[0]}</th><th class="px-3 py-2.5 font-semibold">${head[1]}</th>
- </tr></thead>
- <tbody>${rows.map(([k, v]) => `<tr class="border-t border-line"><th class="px-3 py-3 text-left font-normal text-ink2 align-top">${k}</th><td class="px-3 py-3 break-words">${v}</td></tr>`).join('')}</tbody>
- </table>
- </div>`;
- const AI_FOOT = `
- <p class="mt-2 text-[13px] text-faint">内容由 AI 生成</p>
- <div class="flex gap-5 mt-2 text-muted text-[17px]"><i class="fa-solid fa-volume-low"></i><i class="fa-regular fa-copy"></i><i class="fa-solid fa-arrow-up-from-bracket"></i></div>`;
- const ASK_CARD = `
- <div class="rounded-t-2xl bg-[#f5f7f9] pt-3 pb-2">
- <div class="flex items-center justify-between px-4 text-[15px] text-muted">
- <span class="flex items-center gap-1.5"><i class="fa-solid fa-chevron-left text-[12px]"></i>2 / 2<i class="fa-solid fa-chevron-right text-[12px]"></i></span>
- <span class="w-9 h-9 rounded-full bg-[#e6edf2] flex items-center justify-center text-ink2"><i class="fa-solid fa-xmark text-[13px]"></i></span>
- </div>
- <div class="px-4 mt-1">
- <div class="text-[14px] font-semibold text-[#6b5ce7]">执行时间</div>
- <div class="text-[20px] font-semibold mt-1">希望每天什么时间执行?</div>
- </div>
- </div>
- <div class="bg-white">
- ${['早上 9:00', '早上 8:00', '晚上 21:00'].map((t, i) => `
- <div class="flex items-center gap-3 px-5 py-[18px] border-t border-line">
- <span class="w-[26px] h-[26px] rounded-full flex items-center justify-center text-[13px] ${i === 0 ? 'bg-brand text-white' : 'border border-[#d6d6da] text-muted'}">${i + 1}</span>
- <span class="text-[18px]">${t}</span>
- </div>`).join('')}
- </div>
- <div class="px-4 pt-3 pb-4 bg-white">
- <div class="h-[52px] rounded-2xl bg-[#f5f5f7] flex items-center pl-4 pr-1.5 text-[17px] text-faint">或输入其他选项
- <span class="ml-auto w-10 h-10 rounded-full bg-[#1c1c1e] text-white flex items-center justify-center"><i class="fa-solid fa-arrow-up text-[16px]"></i></span>
- </div>
- </div>`;
- const CHAT_BODIES = {
- preview: USER_BUBBLE() + DONE_ROW + FOLD('共 3 段过程', '收起') + STEP('done') + `
- <p class="mt-3 text-[17px] leading-[1.65]">我来帮你设置这个自动化任务。先确认一下关键参数:</p>
- ${STEP('done')}
- <p class="mt-3 text-[17px] leading-[1.65]">明白了:仅今天执行一次,时间早上 9:00。当前是凌晨 3:31,今天 9:00 还没到,可以正常预约。</p>
- <p class="mt-3 text-[17px] leading-[1.65]">按照流程,先给你看一下操作预览:</p>
- <div class="font-semibold text-[17px] mt-3 mb-2">- 操作预览 - 创建任务</div>
- ${TABLE(['项目', '内容'], [
- ['名称', '今日AI资讯收集'],
- ['频率类型', '<b>once</b>(单次执行)'],
- ['执行时间', '2026-08-03 09:00(Cron: 0 0 9 ...'],
- ['时区', 'Asia/Shanghai'],
- ['生效范围', '仅 2026-08-03 当天'],
- ['超时/重试', '300 秒 / 3 次'],
- ])}
- <p class="mt-3 text-[17px] leading-[1.65]">确认创建请回复"确认",需要调整请直接说明。</p>
- ${AI_FOOT}`,
- typing: USER_BUBBLE() + FOLD('已折叠 2 段过程', '展开') + STEP('running') + `
- <div class="mt-4 flex items-center gap-2 text-[16px] text-muted"><span class="w-[7px] h-[7px] rounded-full bg-[#8fcfb9]"></span>正在整理答案...</div>
- <div class="mt-4 flex gap-1"><span class="w-[6px] h-[6px] rounded-full bg-[#b9b9be]"></span><span class="w-[6px] h-[6px] rounded-full bg-[#c6c6cb]"></span><span class="w-[6px] h-[6px] rounded-full bg-[#d4d4d8]"></span></div>`,
- done: `
- <div class="flex justify-end mb-3"><div class="bg-fill rounded-[20px] rounded-tr-md px-4 py-3 text-[16px]">确认</div></div>
- ${DONE_ROW}
- ${FOLD('已折叠 1 段过程', '展开')}
- <div class="flex items-center gap-2 mt-3 text-[17px] font-semibold"><span class="text-[#12b76a] text-[18px]"><i class="fa-solid fa-circle-check"></i></span>任务创建成功!</div>
- ${TABLE(['项目', '内容'], [
- ['任务 ID', '4631744'],
- ['名称', '今日AI资讯收集'],
- ['状态', '已启用'],
- ['执行时间', '今天 09:00(2026-08-03 09:00:00 +'],
- ])}
- <p class="mt-3 text-[17px] leading-[1.65]">到点后我会自动搜索今日 AI 领域的重要动态(大模型发布、行业新闻、政策监管、融资、技术突破等),并整理成一份中文简报推送给你。</p>
- <div class="mt-3 border-l-[3px] border-[#e6e6ea] pl-3 text-[16px] leading-[1.65] text-ink2">💡 如果之后觉得每天都需要这份资讯,随时跟我说"改成每天 9 点执行",我可以帮你调整为长期任务。</div>
- ${AI_FOOT}`,
- ask: USER_BUBBLE() + DONE_ROW + FOLD('共 3 段过程', '收起') + `
- <p class="mt-3 text-[17px] leading-[1.65]">我来帮你设置这个自动化任务。先确认一下关键参数:</p>
- <p class="mt-3 text-[17px] leading-[1.65]">明白了:仅今天执行一次,时间早上 9:00。当前是凌晨 3:31,今天 9:00 还没到,可以正常预约。</p>`,
- };
- /* ============ 渲染 ============ */
- const wall = document.getElementById('wall');
- const tpl = document.getElementById('tpl-phone');
- const P = el => el && el.closest ? el.closest('.phone') : null;
- PAGES.forEach(page => {
- const wrap = document.createElement('section');
- wrap.className = 'flex flex-col gap-3';
- wrap.dataset.tag = page.tag;
- wrap.innerHTML = `
- <div class="flex items-baseline justify-between px-1 w-[390px]">
- <div class="text-[15px] font-semibold">${page.title}</div>
- <span class="text-[11px] px-2 py-[2px] rounded-full ${page.tag === 'full' ? 'bg-brandsoft text-brand' : 'bg-white text-ink2 border border-line'}">${page.tag === 'full' ? '完整版' : '基础版'}</span>
- </div>
- <div>${tpl.innerHTML}</div>
- <p class="text-[12px] text-ink2/60 px-1 leading-relaxed w-[390px]">${page.desc}</p>`;
- wall.appendChild(wrap);
- const phone = wrap.querySelector('.phone');
- phone.querySelectorAll('.screen').forEach(s => s.classList.toggle('on', s.dataset.screen === (page.screen || 'intro')));
- (page.open || []).forEach(l => phone.querySelector(`[data-layer="${l}"]`).classList.remove('hide'));
- if (page.custom === 'models') phone.querySelector('[data-layer="models"]').classList.remove('hide');
- if (page.custom === 'more') phone.querySelector('[data-layer="more"]').classList.remove('hide');
- if (CHAT_BODIES[page.custom]) {
- phone.querySelector('[data-chat-title]').textContent = '收集今日AI资讯';
- phone.querySelector('[data-chat-scroll]').innerHTML = CHAT_BODIES[page.custom];
- }
- // 追问态:卡片贴在输入框上方,输入栏隐藏(截图中输入框被追问面板取代)
- if (page.custom === 'typing' || page.custom === 'ask') {
- const bar = phone.querySelector('[data-composer]');
- const card = document.createElement('div');
- card.className = 'px-3';
- card.innerHTML = ASK_CARD;
- bar.parentNode.insertBefore(card, bar);
- bar.classList.add('hidden');
- }
- renderModels(phone);
- });
- /* ============ 模型面板 ============ */
- function renderModels(phone) {
- if (phone._sel === undefined) phone._sel = 0;
- const list = phone.querySelector('[data-model-list]');
- list.innerHTML = '';
- MODELS.forEach((m, i) => {
- const sel = i === phone._sel;
- const row = document.createElement('button');
- row.className = 'w-full flex items-center gap-3 py-[15px] text-left';
- row.innerHTML = `
- ${m.icon === 'auto'
- ? '<span class="w-8 h-8 rounded-full border-[1.5px] border-ink flex items-center justify-center text-[15px] font-semibold">A</span>'
- : `<img src="${m.icon}" class="w-8 h-8 rounded-lg object-cover" alt="">`}
- <span class="text-[18px] ${sel ? 'text-brand' : ''}">${m.name}</span>
- <span class="flex gap-1">${m.tags.map(t => `<span class="text-[12px] px-1.5 py-[2px] rounded ${t === '推理' ? 'bg-[#eaeefc] text-reasoning' : 'bg-[#e6f5ec] text-vision'}">${t}</span>`).join('')}</span>
- ${sel ? '<i class="fa-solid fa-check ml-auto text-brand"></i>' : ''}`;
- row.onclick = () => {
- phone._sel = i;
- const name = phone.querySelector('[data-model-name]');
- if (name) name.textContent = m.name;
- const icon = phone.querySelector('[data-model-icon]');
- if (icon) icon.innerHTML = m.icon === 'auto' ? '<i class="fa-regular fa-circle-user text-[16px]"></i>' : `<img src="${m.icon}" class="w-5 h-5 rounded-md" alt="">`;
- renderModels(phone);
- const phoneRef = phone;
- setTimeout(() => { phoneRef.querySelector('[data-layer="models"]').classList.add('hide'); toast(phoneRef, '已切换到 ' + m.name); }, 160);
- };
- list.appendChild(row);
- });
- }
- /* ============ 交互 ============ */
- const openModelsIn = el => P(el).querySelector('[data-layer="models"]').classList.remove('hide');
- const closeModelsIn = el => P(el).querySelector('[data-layer="models"]').classList.add('hide');
- const openMoreIn = el => P(el).querySelector('[data-layer="more"]').classList.remove('hide');
- const closeMoreIn = el => P(el).querySelector('[data-layer="more"]').classList.add('hide');
- const openSideIn = el => P(el).querySelector('[data-layer="side"]').classList.remove('hide');
- const closeSideIn = el => P(el).querySelector('[data-layer="side"]').classList.add('hide');
- function enterChat(el, title) {
- const phone = P(el);
- if (phone.querySelector('.screen.on').dataset.screen === 'chat') {
- phone.querySelector('[data-chat-title]').textContent = title;
- }
- phone.querySelector('[data-layer="side"]').classList.add('hide');
- phone.querySelectorAll('.screen').forEach(s => s.classList.toggle('on', s.dataset.screen === 'chat'));
- phone.querySelector('[data-chat-scroll]').scrollTop = 0;
- }
- const openChat = enterChat;
- function composerInput(el) {
- P(el).querySelector('[data-send]').classList.toggle('hide', !el.value.trim());
- }
- function toast(el, msg) {
- const phone = P(el);
- if (!phone) return;
- let t = phone.querySelector('[data-toast]');
- if (!t) {
- t = document.createElement('div');
- t.dataset.toast = '';
- t.className = 'absolute z-[80] left-1/2 -translate-x-1/2 bottom-28 bg-black/80 text-white text-[13px] px-4 py-2 rounded-xl animate-pop';
- phone.appendChild(t);
- }
- t.textContent = msg;
- clearTimeout(t._t);
- t._t = setTimeout(() => t.remove(), 1500);
- }
- function filter(tag) {
- document.querySelectorAll('[data-f]').forEach(b => {
- const on = b.dataset.f === tag;
- b.className = `px-4 py-2 rounded-full ${on ? 'bg-ink text-white' : 'bg-white border border-line'}`;
- });
- wall.querySelectorAll('section').forEach(s => s.classList.toggle('hide', tag !== 'all' && s.dataset.tag !== tag));
- }
- filter('all');
- </script>
- </body>
- </html>
|