style: apply Taobao orange RPA prototype theme

This commit is contained in:
大森 2026-06-26 18:08:02 +08:00
parent d65e592956
commit 7551f5d123

View File

@ -7,70 +7,115 @@
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script> <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
<title>RPA工作站 · 高保真交互原型</title> <title>RPA工作站 · 高保真交互原型</title>
<style> <style>
:root { --primary:#4f46e5; } :root {
--primary:#ff5000; --rpa-primary:#ff5000; --rpa-primary-hover:#e64500; --rpa-primary-soft:#fff3e8;
--rpa-tech-blue:#ff8a00; --rpa-tech-blue-hover:#ff9d2e; --rpa-nav-bg:#373942; --rpa-nav-panel:#2b2d34;
--rpa-nav-border:#383a45; --rpa-hosting-bg:#0a091e; --rpa-bg:#f7f8fa; --rpa-surface:#ffffff;
--rpa-surface-subtle:#f3f4f6; --rpa-border:#e6e6e6; --rpa-border-strong:#cbd5e1; --rpa-text:#333333;
--rpa-heading:#1f2937; --rpa-muted:#666666; --rpa-subtle:#7c7c7c; --rpa-inverse:#e5e9ee;
--rpa-inverse-muted:#a3a3a3; --rpa-success:#4ab933; --rpa-success-bg:#edffe9; --rpa-success-border:#dcf9d6;
--rpa-info:#28a3ef; --rpa-info-bg:#eef8ff; --rpa-warning:#f59e0b; --rpa-warning-bg:#fffbeb;
--rpa-danger:#d44044; --rpa-danger-bg:#fff2f2; --rpa-danger-border:#ffe3e0;
--rpa-shadow-card:0 1px 2px rgba(15,23,42,.06); --rpa-shadow-float:0 8px 24px rgba(15,23,42,.14);
--rpa-shadow-blocking:0 24px 60px rgba(0,0,0,.35); --rpa-font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;
}
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { overflow: hidden; } body { overflow: hidden; color: var(--rpa-text); }
.scrollbar-thin::-webkit-scrollbar { width: 8px; height: 8px; } .scrollbar-thin::-webkit-scrollbar { width: 8px; height: 8px; }
.scrollbar-thin::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 999px; } .scrollbar-thin::-webkit-scrollbar-thumb { background: var(--rpa-border-strong); border-radius: 999px; }
.focus-ring:focus-visible { outline: none; box-shadow: 0 0 0 2px #6366f1; } .focus-ring:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--rpa-primary); }
.rpa-card { border:1px solid var(--rpa-border); background:var(--rpa-surface); border-radius:1rem; box-shadow:var(--rpa-shadow-card); }
.rpa-panel { border:1px solid var(--rpa-border); background:var(--rpa-surface); border-radius:1rem; box-shadow:var(--rpa-shadow-card); }
.rpa-table { overflow:hidden; border:1px solid var(--rpa-border); background:var(--rpa-surface); border-radius:1rem; box-shadow:var(--rpa-shadow-card); }
.rpa-table thead { background:var(--rpa-surface-subtle); color:var(--rpa-muted); }
.rpa-table tbody { color:var(--rpa-text); }
.rpa-table tbody tr { border-top:1px solid var(--rpa-border); }
.rpa-table tbody tr:hover { background:#f8fafc; }
.rpa-scrim { background:rgba(10,9,30,.55); }
.rpa-hosting-scrim { background:rgba(10,9,30,.85); }
.rpa-primary-btn { background:var(--rpa-primary); color:#fff; box-shadow:0 8px 20px rgba(255,80,0,.22); }
.rpa-primary-btn:hover { background:var(--rpa-primary-hover); }
.rpa-secondary-btn { border:1px solid var(--rpa-border); background:var(--rpa-surface); color:var(--rpa-text); }
.rpa-secondary-btn:hover { background:var(--rpa-surface-subtle); }
.rpa-danger-btn { background:var(--rpa-danger); color:#fff; box-shadow:0 8px 20px rgba(212,64,68,.22); }
.rpa-danger-btn:hover { filter:brightness(.95); }
.rpa-link-btn { color:var(--rpa-primary); }
.rpa-link-btn:hover { color:var(--rpa-primary-hover); }
.rpa-chip { display:inline-flex; align-items:center; gap:.375rem; border-radius:999px; padding:.25rem .625rem; font-size:.75rem; line-height:1rem; font-weight:500; border:1px solid transparent; }
.rpa-chip-primary { background:var(--rpa-primary-soft); color:var(--rpa-primary-hover); border-color:#ffd8bf; }
.rpa-chip-success { background:var(--rpa-success-bg); color:#2f8a20; border-color:var(--rpa-success-border); }
.rpa-chip-info { background:var(--rpa-info-bg); color:#0f7fbd; border-color:#d6efff; }
.rpa-chip-warning { background:var(--rpa-warning-bg); color:#b45309; border-color:#fde68a; }
.rpa-chip-danger { background:var(--rpa-danger-bg); color:var(--rpa-danger); border-color:var(--rpa-danger-border); }
.rpa-chip-neutral { background:var(--rpa-surface-subtle); color:var(--rpa-muted); border-color:var(--rpa-border); }
.rpa-nav-item { color:var(--rpa-inverse-muted); }
.rpa-nav-item:hover { background:rgba(255,255,255,.08); color:var(--rpa-inverse); }
.rpa-nav-item:hover i { color:var(--rpa-tech-blue); }
.rpa-nav-item-active { background:var(--rpa-primary); color:#fff; box-shadow:0 8px 20px rgba(255,80,0,.25); }
.rpa-nav-item-active i { color:#fff; }
.rpa-nav-status { background:var(--rpa-nav-panel); border:1px solid var(--rpa-nav-border); color:var(--rpa-inverse-muted); }
.rpa-code { font-family:var(--rpa-font-mono); background:var(--rpa-surface-subtle); color:var(--rpa-heading); border:1px solid var(--rpa-border); border-radius:.75rem; }
.rpa-progress { background:linear-gradient(90deg, var(--rpa-primary), var(--rpa-tech-blue)); }
.rpa-control, [data-app-root] input, [data-app-root] select, [data-app-root] textarea { border-color:var(--rpa-border-strong); }
[data-app-root] input:focus-visible, [data-app-root] select:focus-visible, [data-app-root] textarea:focus-visible { outline:none; box-shadow:0 0 0 2px var(--rpa-primary); }
</style> </style>
</head> </head>
<body class="min-h-screen bg-gradient-to-b from-indigo-50 to-slate-100 p-6 text-slate-950"> <body class="min-h-screen p-6" style="background:linear-gradient(180deg, #fff3e8 0%, #f7f8fa 45%, #ffffff 100%); color:var(--rpa-text);">
<main class="mx-auto flex h-[calc(100vh-48px)] max-w-[1240px] flex-col items-center gap-4 overflow-hidden"> <main class="mx-auto flex h-[calc(100vh-48px)] max-w-[1240px] flex-col items-center gap-4 overflow-hidden">
<header class="flex w-[1180px] shrink-0 items-center justify-between gap-4"> <header class="flex w-[1180px] shrink-0 items-center justify-between gap-4">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="grid h-10 w-10 place-items-center rounded-2xl bg-[var(--primary)] text-white shadow-lg shadow-indigo-500/25"> <div class="grid h-10 w-10 place-items-center rounded-2xl text-white shadow-lg" style="background:var(--rpa-primary); box-shadow:0 10px 24px rgba(255,80,0,.25)">
<i data-lucide="workflow" class="h-5 w-5"></i> <i data-lucide="workflow" class="h-5 w-5"></i>
</div> </div>
<div> <div>
<h1 class="m-0 text-xl font-semibold text-slate-950">RPA工作站 原型</h1> <h1 class="m-0 text-xl font-semibold" style="color:var(--rpa-heading)">RPA工作站 原型</h1>
<p class="m-0 text-sm text-slate-600">本地优先 AI + RPA 视觉标注与托管执行平台</p> <p class="m-0 text-sm" style="color:var(--rpa-muted)">本地优先 AI + RPA 视觉标注与托管执行平台</p>
</div> </div>
</div> </div>
<div class="flex rounded-full border border-slate-200 bg-white p-1 shadow-sm" aria-label="端切换"> <div class="flex rounded-full border bg-white p-1 shadow-sm" style="border-color:var(--rpa-border)" aria-label="端切换">
<button type="button" data-platform-tab="windows" class="focus-ring h-10 rounded-full bg-[var(--primary)] px-5 text-sm font-medium text-white">Windows 端</button> <button type="button" data-platform-tab="windows" class="focus-ring h-10 rounded-full px-5 text-sm font-medium text-white" style="background:var(--rpa-primary)">Windows 端</button>
<button type="button" data-platform-tab="mac" class="focus-ring h-10 rounded-full px-5 text-sm font-medium text-slate-600">Mac 端</button> <button type="button" data-platform-tab="mac" class="focus-ring h-10 rounded-full px-5 text-sm font-medium" style="color:var(--rpa-muted)">Mac 端</button>
</div> </div>
</header> </header>
<section class="min-h-0 shrink-0" data-platform-panel="windows"> <section class="min-h-0 shrink-0" data-platform-panel="windows">
<div class="flex h-[760px] w-[1180px] flex-col overflow-hidden border border-slate-300 bg-white shadow-2xl"> <div class="flex h-[760px] w-[1180px] flex-col overflow-hidden border border-slate-300 bg-white shadow-2xl">
<div class="flex h-10 shrink-0 items-center border-b border-slate-200 bg-white pl-3"> <div class="flex h-10 shrink-0 items-center border-b border-slate-200 bg-white pl-3">
<div class="flex flex-1 items-center gap-2 text-sm font-medium text-slate-700"> <div class="flex flex-1 items-center gap-2 text-sm font-medium text-[var(--rpa-text)]">
<span class="grid h-5 w-5 place-items-center rounded bg-[var(--primary)] text-white"><i data-lucide="workflow" class="h-3.5 w-3.5"></i></span> <span class="grid h-5 w-5 place-items-center rounded bg-[var(--rpa-primary)] text-white"><i data-lucide="workflow" class="h-3.5 w-3.5"></i></span>
<span>RPA工作站</span> <span>RPA工作站</span>
</div> </div>
<button class="grid h-10 w-12 place-items-center text-slate-600 hover:bg-slate-100" aria-label="最小化"></button> <button class="grid h-10 w-12 place-items-center hover:text-[var(--rpa-tech-blue)]" aria-label="最小化"></button>
<button class="grid h-10 w-12 place-items-center text-slate-600 hover:bg-slate-100" aria-label="最大化"></button> <button class="grid h-10 w-12 place-items-center hover:text-[var(--rpa-tech-blue)]" aria-label="最大化"></button>
<button class="grid h-10 w-12 place-items-center text-slate-600 hover:bg-red-500 hover:text-white" aria-label="关闭">×</button> <button class="grid h-10 w-12 place-items-center text-[var(--rpa-muted)] hover:bg-red-500 hover:text-white" aria-label="关闭">×</button>
</div> </div>
<div class="relative min-h-0 flex-1 overflow-hidden bg-slate-50" data-app-root data-platform="windows"></div> <div class="relative min-h-0 flex-1 overflow-hidden" style="background:var(--rpa-bg)" data-app-root data-platform="windows"></div>
</div> </div>
</section> </section>
<section class="hidden min-h-0 shrink-0" data-platform-panel="mac"> <section class="hidden min-h-0 shrink-0" data-platform-panel="mac">
<div class="flex h-[760px] w-[1180px] flex-col overflow-hidden rounded-[1.75rem] bg-white shadow-2xl shadow-slate-900/20 ring-1 ring-slate-200"> <div class="flex h-[760px] w-[1180px] flex-col overflow-hidden rounded-[1.75rem] bg-white shadow-2xl shadow-slate-900/20 ring-1 ring-slate-200">
<div class="flex h-11 shrink-0 items-center border-b border-slate-200 bg-slate-50/95 px-4"> <div class="flex h-11 shrink-0 items-center border-b border-slate-200 px-4" style="background:var(--rpa-bg)">
<div class="flex gap-2"> <div class="flex gap-2">
<span class="h-3 w-3 rounded-full bg-red-500"></span> <span class="h-3 w-3 rounded-full bg-red-500"></span>
<span class="h-3 w-3 rounded-full bg-yellow-400"></span> <span class="h-3 w-3 rounded-full bg-yellow-400"></span>
<span class="h-3 w-3 rounded-full bg-green-500"></span> <span class="h-3 w-3 rounded-full bg-green-500"></span>
</div> </div>
<div class="flex-1 text-center text-sm font-medium text-slate-600">RPA工作站</div> <div class="flex-1 text-center text-sm font-medium text-[var(--rpa-muted)]">RPA工作站</div>
<div class="w-16"></div> <div class="w-16"></div>
</div> </div>
<div class="relative min-h-0 flex-1 overflow-hidden bg-slate-50" data-app-root data-platform="mac"></div> <div class="relative min-h-0 flex-1 overflow-hidden" style="background:var(--rpa-bg)" data-app-root data-platform="mac"></div>
</div> </div>
</section> </section>
</main> </main>
<div class="hidden" aria-hidden="true" data-page="工作台" data-page-secondary="自动化任务">PRD coverage marker: 名称、类型、索引状态、引用次数、更新时间、权限模式;时间、员工、应用、知识库、任务状态。</div> <div class="hidden" aria-hidden="true" data-page="工作台" data-page-secondary="自动化任务">PRD coverage marker: 名称、类型、索引状态、引用次数、更新时间、权限模式;时间、员工、应用、知识库、任务状态。</div>
<template id="app-template"> <template id="app-template">
<div class="flex h-full overflow-hidden bg-slate-50 text-slate-950"> <div class="flex h-full overflow-hidden" style="background:var(--rpa-bg); color:var(--rpa-text);">
<aside class="flex w-[92px] shrink-0 flex-col items-center gap-2 border-r border-slate-200 bg-slate-950 p-3 text-slate-300" data-nav></aside> <aside class="flex w-[92px] shrink-0 flex-col items-center gap-2 border-r p-3" style="background:var(--rpa-nav-bg); border-color:var(--rpa-nav-border); color:var(--rpa-inverse-muted);" data-nav></aside>
<section class="relative flex min-w-0 flex-1 flex-col overflow-hidden"> <section class="relative flex min-w-0 flex-1 flex-col overflow-hidden">
<header class="flex h-20 shrink-0 items-center justify-between border-b border-slate-200 bg-white px-6" data-header></header> <header class="flex h-20 shrink-0 items-center justify-between border-b px-6" style="border-color:var(--rpa-border); background:var(--rpa-surface);" data-header></header>
<main class="min-h-0 flex-1 overflow-y-auto p-6 scrollbar-thin" data-content></main> <main class="min-h-0 flex-1 overflow-y-auto p-6 scrollbar-thin" style="background:var(--rpa-bg);" data-content></main>
<div class="pointer-events-none absolute right-5 top-24 z-50 space-y-2" data-toasts></div> <div class="pointer-events-none absolute right-5 top-24 z-50 space-y-2" data-toasts></div>
<div class="absolute inset-0 z-40 hidden" data-overlay-layer></div> <div class="absolute inset-0 z-40 hidden" data-overlay-layer></div>
</section> </section>
@ -119,8 +164,8 @@ const prototypeState = {
}; };
function clsBadge(kind){ function clsBadge(kind){
const map = {ready:'bg-emerald-50 text-emerald-700 ring-emerald-200', indexing:'bg-amber-50 text-amber-700 ring-amber-200', pending:'bg-blue-50 text-blue-700 ring-blue-200', failed:'bg-red-50 text-red-700 ring-red-200', running:'bg-indigo-50 text-indigo-700 ring-indigo-200', idle:'bg-slate-100 text-slate-700 ring-slate-200', waiting_user:'bg-orange-50 text-orange-700 ring-orange-200', succeeded:'bg-emerald-50 text-emerald-700 ring-emerald-200', canceled:'bg-slate-100 text-slate-700 ring-slate-200', queued:'bg-blue-50 text-blue-700 ring-blue-200'}; const map = {ready:'rpa-chip rpa-chip-success', succeeded:'rpa-chip rpa-chip-success', indexing:'rpa-chip rpa-chip-warning', waiting_user:'rpa-chip rpa-chip-warning', pending:'rpa-chip rpa-chip-info', queued:'rpa-chip rpa-chip-info', failed:'rpa-chip rpa-chip-danger', running:'rpa-chip rpa-chip-primary', idle:'rpa-chip rpa-chip-neutral', canceled:'rpa-chip rpa-chip-neutral'};
return 'inline-flex items-center rounded-full px-2.5 py-1 text-xs font-medium ring-1 ' + (map[kind] || map.idle); return map[kind] || map.idle;
} }
function icon(name, c='h-4 w-4'){ return `<i data-lucide="${name}" class="${c}"></i>`; } function icon(name, c='h-4 w-4'){ return `<i data-lucide="${name}" class="${c}"></i>`; }
function toast(text, type='success'){ prototypeState.toast.push({text,type,id:Date.now()}); setTimeout(()=>{ prototypeState.toast.shift(); renderAll(); }, 2400); renderAll(); } function toast(text, type='success'){ prototypeState.toast.push({text,type,id:Date.now()}); setTimeout(()=>{ prototypeState.toast.shift(); renderAll(); }, 2400); renderAll(); }
@ -133,16 +178,17 @@ function init(){
function renderRoot(root){ function renderRoot(root){
root.dataset.appRoot = ''; root.dataset.appRoot = '';
const nav = root.querySelector('[data-nav]'); const nav = root.querySelector('[data-nav]');
nav.innerHTML = `<div class="flex h-full w-full flex-col"><div class="flex flex-1 flex-col items-center gap-2">${pages.map(([p,ic]) => { const active = prototypeState.page===p; return `<button type="button" data-page="${p}" aria-label="${p}" aria-current="${active?'page':'false'}" class="focus-ring group flex min-h-[56px] w-full flex-col items-center justify-center gap-1 rounded-2xl text-[11px] transition ${active?'bg-indigo-500 text-white shadow-lg shadow-indigo-500/25':'text-slate-400 hover:bg-white/10 hover:text-indigo-100'}"><span class="${active?'text-white':'text-slate-400 group-hover:text-indigo-200'}">${icon(ic,'h-5 w-5')}</span><span>${p}</span></button>`; }).join('')}</div><div class="mt-2 flex w-full flex-col items-center rounded-2xl bg-slate-900/90 px-2 py-2 text-[10px] leading-4 text-slate-400 ring-1 ring-white/5"><span class="mb-1 text-emerald-400">${icon('circle-dot','h-3.5 w-3.5')}</span><span>本地在线</span></div></div>`; const hosting = prototypeState.rpaHosting;
nav.innerHTML = `<div class="flex h-full w-full flex-col"><div class="flex flex-1 flex-col items-center gap-2">${pages.map(([p,ic]) => { const active = prototypeState.page===p; return `<button type="button" data-page="${p}" aria-label="${p}" aria-current="${active?'page':'false'}" class="focus-ring group flex min-h-[56px] w-full flex-col items-center justify-center gap-1 rounded-2xl text-[11px] transition ${active?'rpa-nav-item-active':'rpa-nav-item'}"><span>${icon(ic,'h-5 w-5')}</span><span>${p}</span></button>`; }).join('')}</div><div class="rpa-nav-status mt-2 flex w-full flex-col items-center rounded-2xl px-2 py-2 text-[10px] leading-4"><span class="mb-1" style="color:${hosting?'var(--rpa-warning)':'var(--rpa-success)'}">${icon('circle-dot','h-3.5 w-3.5')}</span><span>${hosting?'托管中':'本地在线'}</span></div></div>`;
const header = root.querySelector('[data-header]'); const header = root.querySelector('[data-header]');
header.innerHTML = `<div><h2 class="text-xl font-semibold">${prototypeState.page}</h2><p class="mt-1 text-sm text-slate-500">${pageSummaries[prototypeState.page]}</p></div><div class="flex flex-wrap justify-end gap-2">${chip('模型 在线','activity','emerald')}${chip(prototypeState.rpaHosting?'RPA 托管中':'RPA 空闲','radio','indigo')}${chip('本地模式','hard-drive','slate')}${chip('试用 14天','badge-check','amber')}</div>`; header.innerHTML = `<div><h2 class="text-xl font-semibold" style="color:var(--rpa-heading)">${prototypeState.page}</h2><p class="mt-1 text-sm" style="color:var(--rpa-muted)">${pageSummaries[prototypeState.page]}</p></div><div class="flex flex-wrap justify-end gap-2">${chip('模型 在线','activity','emerald')}${chip(prototypeState.rpaHosting?'RPA 托管中':'RPA 空闲','radio','indigo')}${chip('本地模式','hard-drive','slate')}${chip('试用 14天','badge-check','amber')}</div>`;
root.querySelector('[data-content]').innerHTML = renderPage(); root.querySelector('[data-content]').innerHTML = renderPage();
root.querySelector('[data-toasts]').innerHTML = prototypeState.toast.map(t => `<div class="pointer-events-auto flex min-w-[280px] items-start gap-2 rounded-xl border bg-white px-4 py-3 text-sm shadow-xl ${t.type==='error'?'border-red-200 text-red-700':'border-emerald-200 text-slate-700'}">${icon(t.type==='error'?'circle-alert':'circle-check','mt-0.5 h-4 w-4 shrink-0')}<span>${t.text}</span></div>`).join(''); root.querySelector('[data-toasts]').innerHTML = prototypeState.toast.map(t => `<div class="pointer-events-auto flex min-w-[280px] items-start gap-2 rounded-xl border px-4 py-3 text-sm shadow-xl" style="background:${t.type==='error'?'var(--rpa-danger-bg)':'var(--rpa-success-bg)'}; border-color:${t.type==='error'?'var(--rpa-danger-border)':'var(--rpa-success-border)'}; color:${t.type==='error'?'var(--rpa-danger)':'var(--rpa-text)'}">${icon(t.type==='error'?'circle-alert':'circle-check','mt-0.5 h-4 w-4 shrink-0')}<span>${t.text}</span></div>`).join('');
renderOverlay(root); renderOverlay(root);
} }
function chip(text, ic, tone){ return `<span class="inline-flex h-8 items-center gap-1.5 rounded-full bg-${tone==='slate'?'slate':'white'}-${tone==='slate'?'100':'50'} px-3 text-xs font-medium text-${tone}-700 ring-1 ring-${tone}-200">${icon(ic)}${text}</span>`; } function chip(text, ic, tone){ const map={emerald:'rpa-chip-success', indigo:'rpa-chip-primary', slate:'rpa-chip-neutral', amber:'rpa-chip-warning'}; return `<span class="rpa-chip ${map[tone] || 'rpa-chip-neutral'}">${icon(ic)}${text}</span>`; }
function card(title, body, footer=''){ return `<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"><h3 class="mb-4 flex items-center gap-2 font-semibold text-slate-900">${body.icon||''}${title}</h3>${body.html||body}${footer}</article>`; } function card(title, body, footer=''){ return `<article class="rpa-card p-5"><h3 class="mb-4 flex items-center gap-2 font-semibold" style="color:var(--rpa-heading)"><span style="color:var(--rpa-primary)">${body.icon||''}</span>${title}</h3>${body.html||body}${footer}</article>`; }
function actionBtn(label, attrs='', primary=false){ return `<button type="button" ${attrs} class="focus-ring inline-flex h-10 items-center justify-center gap-2 rounded-xl ${primary?'bg-[var(--primary)] text-white shadow-lg shadow-indigo-500/20':'border border-slate-200 bg-white text-slate-700 hover:bg-slate-50'} px-4 text-sm font-medium">${label}</button>`; } function actionBtn(label, attrs='', primary=false){ return `<button type="button" ${attrs} class="focus-ring inline-flex h-10 items-center justify-center gap-2 rounded-xl ${primary?'rpa-primary-btn':'rpa-secondary-btn'} px-4 text-sm font-medium">${label}</button>`; }
function renderPage(){ function renderPage(){
if (prototypeState.page==='工作台') return dashboardPage(); if (prototypeState.page==='工作台') return dashboardPage();
@ -155,36 +201,36 @@ function renderPage(){
function dashboardPage(){ function dashboardPage(){
return `<div class="space-y-6"> return `<div class="space-y-6">
<div class="grid grid-cols-3 gap-4"> <div class="grid grid-cols-3 gap-4">
${card('环境状态',{icon:icon('shield-check'),html:`<div class="space-y-3 text-sm"><div class="flex justify-between"><span>权限</span><b>3/4</b></div><div class="flex justify-between"><span>模型连通</span><span class="text-emerald-600">通过</span></div><div class="flex justify-between"><span>本地 GPU</span><span>中档</span></div><div class="flex justify-between"><span>存储</span><span class="text-emerald-600">可用</span></div>${actionBtn('查看首次配置','data-modal-open="config"')}</div>`})} ${card('环境状态',{icon:icon('shield-check'),html:`<div class="space-y-3 text-sm"><div class="flex justify-between"><span>权限</span><b>3/4</b></div><div class="flex justify-between"><span>模型连通</span><span class="text-[var(--rpa-success)]">通过</span></div><div class="flex justify-between"><span>本地 GPU</span><span>中档</span></div><div class="flex justify-between"><span>存储</span><span class="text-[var(--rpa-success)]">可用</span></div>${actionBtn('查看首次配置','data-modal-open="config"')}</div>`})}
${card('资产状态',{icon:icon('boxes'),html:`<div class="grid grid-cols-2 gap-3 text-sm"><b>知识库 3</b><b>就绪索引 2</b><b>AI 员工 2</b><b>标注应用 1</b></div>`})} ${card('资产状态',{icon:icon('boxes'),html:`<div class="grid grid-cols-2 gap-3 text-sm"><b>知识库 3</b><b>就绪索引 2</b><b>AI 员工 2</b><b>标注应用 1</b></div>`})}
${card('执行状态',{icon:icon('activity'),html:`<div class="space-y-3 text-sm"><div class="flex justify-between"><span>运行中</span><b>0</b></div><div class="flex justify-between"><span>失败任务</span><b class="text-red-600">1</b></div><div class="flex justify-between"><span>最近托管</span><span class="text-emerald-600">成功</span></div><div class="flex justify-between"><span>RPA</span><span>${prototypeState.rpaHosting?'托管中':'空闲'}</span></div></div>`})} ${card('执行状态',{icon:icon('activity'),html:`<div class="space-y-3 text-sm"><div class="flex justify-between"><span>运行中</span><b>0</b></div><div class="flex justify-between"><span>失败任务</span><b class="text-[var(--rpa-danger)]">1</b></div><div class="flex justify-between"><span>最近托管</span><span class="text-[var(--rpa-success)]">成功</span></div><div class="flex justify-between"><span>RPA</span><span>${prototypeState.rpaHosting?'托管中':'空闲'}</span></div></div>`})}
</div> </div>
<section class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"><h3 class="mb-4 font-semibold">快速入口</h3><div class="flex flex-wrap gap-3"> <section class="rpa-panel p-5"><h3 class="mb-4 font-semibold">快速入口</h3><div class="flex flex-wrap gap-3">
${actionBtn(icon('database')+'创建知识库','data-modal-open="knowledge"',true)} ${actionBtn(icon('database')+'创建知识库','data-modal-open="knowledge"',true)}
${actionBtn(icon('bot')+'创建 AI 员工','data-modal-open="agent"',true)} ${actionBtn(icon('bot')+'创建 AI 员工','data-modal-open="agent"',true)}
${actionBtn(icon('scan-line')+'进入标注模式 Ctrl+E','data-overlay-open="annotation"')} ${actionBtn(icon('scan-line')+'进入标注模式 Ctrl+E','data-overlay-open="annotation"')}
${actionBtn(icon('play')+'启动自动化任务','data-modal-open="hosting-confirm"')} ${actionBtn(icon('play')+'启动自动化任务','data-modal-open="hosting-confirm"')}
${actionBtn(icon('package-check')+'导出诊断包','data-action="diagnostic"')} ${actionBtn(icon('package-check')+'导出诊断包','data-action="diagnostic"')}
</div></section> </div></section>
<section class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"><h3 class="mb-4 font-semibold">核心闭环</h3><div class="flex items-center gap-2 text-sm text-slate-600">${['首次配置','知识库','AI 员工','标注应用','自动化任务','托管执行','审计复盘'].map((s,i)=>`<div class="flex items-center gap-2"><span class="grid h-8 w-8 place-items-center rounded-full bg-indigo-50 font-semibold text-indigo-700">${i+1}</span><span>${s}</span></div>${i<6?'<div class="h-px w-8 bg-slate-200"></div>':''}`).join('')}</div></section> <section class="rpa-panel p-5"><h3 class="mb-4 font-semibold">核心闭环</h3><div class="flex items-center gap-2 text-sm text-[var(--rpa-muted)]">${['首次配置','知识库','AI 员工','标注应用','自动化任务','托管执行','审计复盘'].map((s,i)=>`<div class="flex items-center gap-2"><span class="grid h-8 w-8 place-items-center rounded-full font-semibold" style="background:var(--rpa-primary-soft); color:var(--rpa-primary)">${i+1}</span><span>${s}</span></div>${i<6?'<div class="h-px w-8 bg-slate-200"></div>':''}`).join('')}</div></section>
</div>`; </div>`;
} }
function kbPage(){ function kbPage(){
return `<div class="space-y-5"><div class="flex items-center gap-3 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm"><div class="relative flex-1">${icon('search','absolute left-3 top-3 h-4 w-4 text-slate-400')}<input class="focus-ring h-10 w-full rounded-xl border border-slate-200 pl-9 text-sm" placeholder="搜索制度、流程、操作说明"></div>${actionBtn('来源','')}${actionBtn('类型','')}${actionBtn('按更新时间','')}${actionBtn(icon('upload')+'上传/创建知识库','data-modal-open="knowledge"',true)}</div>${table(['名称','类型','索引状态','引用次数','更新时间','权限模式'], prototypeState.kbs.map((k,i)=>[`<div><b>${k.name}</b><p class="text-xs text-slate-500">${k.description}${k.error?` · ${k.error} ${actionBtn('重试',`data-action="retry-kb" data-index="${i}"`)}`:''}</p></div>`,k.type,`<span class="${clsBadge(k.status)}">${k.status}</span>`,k.refs,k.updated,`<button data-action="permission" data-index="${i}" class="${clsBadge('idle')}">${k.permission}</button>`]))}</div>`; return `<div class="space-y-5"><div class="flex items-center gap-3 rpa-panel p-4"><div class="relative flex-1">${icon('search','absolute left-3 top-3 h-4 w-4 text-slate-400')}<input class="focus-ring h-10 w-full rounded-xl border border-slate-200 pl-9 text-sm" placeholder="搜索制度、流程、操作说明"></div>${actionBtn('来源','')}${actionBtn('类型','')}${actionBtn('按更新时间','')}${actionBtn(icon('upload')+'上传/创建知识库','data-modal-open="knowledge"',true)}</div>${table(['名称','类型','索引状态','引用次数','更新时间','权限模式'], prototypeState.kbs.map((k,i)=>[`<div><b>${k.name}</b><p class="text-xs text-[var(--rpa-muted)]">${k.description}${k.error?` · ${k.error} ${actionBtn('重试',`data-action="retry-kb" data-index="${i}"`)}`:''}</p></div>`,k.type,`<span class="${clsBadge(k.status)}">${k.status}</span>`,k.refs,k.updated,`<button data-action="permission" data-index="${i}" class="${clsBadge('idle')}">${k.permission}</button>`]))}</div>`;
} }
function agentPage(){ function agentPage(){
return `<div class="space-y-5"><div class="flex justify-between rounded-2xl border border-slate-200 bg-white p-4 shadow-sm"><div><h3 class="font-semibold">AI 员工</h3><p class="text-sm text-slate-500">启动任务前必须展示知识库、应用和停止方式。</p></div>${actionBtn(icon('plus')+'创建 AI 员工','data-modal-open="agent"',true)}</div>${table(['名称','角色','状态','可访问知识库','可操作标注应用','最近执行时间','操作'], prototypeState.agents.map((a,i)=>[a.name,a.role,`<span class="${clsBadge(a.status)}">${a.status}</span>`,a.kb,a.apps,a.last,actionBtn('启动任务',`data-modal-open="agent-confirm" data-index="${i}"`)]))}</div>`; return `<div class="space-y-5"><div class="flex justify-between rpa-panel p-4"><div><h3 class="font-semibold">AI 员工</h3><p class="text-sm text-[var(--rpa-muted)]">启动任务前必须展示知识库、应用和停止方式。</p></div>${actionBtn(icon('plus')+'创建 AI 员工','data-modal-open="agent"',true)}</div>${table(['名称','角色','状态','可访问知识库','可操作标注应用','最近执行时间','操作'], prototypeState.agents.map((a,i)=>[a.name,a.role,`<span class="${clsBadge(a.status)}">${a.status}</span>`,a.kb,a.apps,a.last,actionBtn('启动任务',`data-modal-open="agent-confirm" data-index="${i}"`)]))}</div>`;
} }
function annotationPage(){ function annotationPage(){
const tab = prototypeState.activeAppTab; const tab = prototypeState.activeAppTab;
const tabHtml = ['概览','窗口','节点','流程'].map(t=>`<button type="button" data-tab="${t}" class="focus-ring h-10 rounded-xl px-4 text-sm font-medium ${tab===t?'bg-indigo-600 text-white':'text-slate-600 hover:bg-slate-100'}">${t}</button>`).join(''); const tabHtml = ['概览','窗口','节点','流程'].map(t=>`<button type="button" data-tab="${t}" class="focus-ring h-10 rounded-xl px-4 text-sm font-medium ${tab===t?'rpa-primary-btn':'hover:text-[var(--rpa-tech-blue)]'}">${t}</button>`).join('');
let panel = tab==='节点' ? table(['节点名','node_type','bounds','allowed_actions','confidence'], prototypeState.nodes.map(n=>[n.name,n.type,n.bounds,n.actions,n.confidence])) : tab==='窗口' ? `<div class="grid grid-cols-2 gap-4 text-sm"><div class="rounded-xl border p-4"><b>主窗口</b><p>1180×760 · 已标注</p></div><div class="rounded-xl border p-4"><b>审批弹窗</b><p>520×360 · 已标注</p></div></div>` : tab==='流程' ? `<ol class="space-y-3 text-sm"><li>1. 读取制度规则</li><li>2. 填写金额输入框</li><li>3. 点击提交按钮前暂停确认</li></ol>` : `<div class="grid grid-cols-4 gap-4 text-sm"><div class="rounded-xl border p-4"><b>窗口数</b><p>2</p></div><div class="rounded-xl border p-4"><b>节点数</b><p>${prototypeState.nodes.length}</p></div><div class="rounded-xl border p-4"><b>流程数</b><p>3</p></div><div class="rounded-xl border p-4"><b>最近更新</b><p>今天 09:20</p></div></div>`; let panel = tab==='节点' ? table(['节点名','node_type','bounds','allowed_actions','confidence'], prototypeState.nodes.map(n=>[n.name,n.type,n.bounds,n.actions,n.confidence])) : tab==='窗口' ? `<div class="grid grid-cols-2 gap-4 text-sm"><div class="rounded-xl border p-4"><b>主窗口</b><p>1180×760 · 已标注</p></div><div class="rounded-xl border p-4"><b>审批弹窗</b><p>520×360 · 已标注</p></div></div>` : tab==='流程' ? `<ol class="space-y-3 text-sm"><li>1. 读取制度规则</li><li>2. 填写金额输入框</li><li>3. 点击提交按钮前暂停确认</li></ol>` : `<div class="grid grid-cols-4 gap-4 text-sm"><div class="rounded-xl border p-4"><b>窗口数</b><p>2</p></div><div class="rounded-xl border p-4"><b>节点数</b><p>${prototypeState.nodes.length}</p></div><div class="rounded-xl border p-4"><b>流程数</b><p>3</p></div><div class="rounded-xl border p-4"><b>最近更新</b><p>今天 09:20</p></div></div>`;
return `<div class="space-y-5"><div class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"><div class="flex items-center justify-between"><div><h3 class="font-semibold">企业报销系统</h3><p class="text-sm text-slate-500">窗口数 2、节点数 ${prototypeState.nodes.length}、流程数 3、最近更新 今天</p></div>${actionBtn(icon('scan-line')+'进入标注模式 Ctrl+E','data-overlay-open="annotation"',true)}</div></div><section class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"><div class="mb-5 flex gap-2">${tabHtml}</div>${panel}</section></div>`; return `<div class="space-y-5"><div class="rpa-panel p-5"><div class="flex items-center justify-between"><div><h3 class="font-semibold">企业报销系统</h3><p class="text-sm text-[var(--rpa-muted)]">窗口数 2、节点数 ${prototypeState.nodes.length}、流程数 3、最近更新 今天</p></div>${actionBtn(icon('scan-line')+'进入标注模式 Ctrl+E','data-overlay-open="annotation"',true)}</div></div><section class="rpa-panel p-5"><div class="mb-5 flex gap-2">${tabHtml}</div>${panel}</section></div>`;
} }
function tasksPage(){ function tasksPage(){
const rows = prototypeState.tasks.map((t,i)=>[t.name,t.type,`<span class="${clsBadge(t.status)}">${t.status}</span>`,t.employee,t.app,t.updated,t.error?`<div class="text-red-700">${t.error}<br><code class="text-xs">${t.code}</code><br>${actionBtn('重试',`data-action="retry-task" data-index="${i}"`)}</div>`:actionBtn('查看','')]); const rows = prototypeState.tasks.map((t,i)=>[t.name,t.type,`<span class="${clsBadge(t.status)}">${t.status}</span>`,t.employee,t.app,t.updated,t.error?`<div class="text-[var(--rpa-danger)]">${t.error}<br><code class="text-xs">${t.code}</code><br>${actionBtn('重试',`data-action="retry-task" data-index="${i}"`)}</div>`:actionBtn('查看','')]);
let audits = prototypeState.audits.filter(a=>(!prototypeState.auditFilters.employee||a.employee===prototypeState.auditFilters.employee)&&(!prototypeState.auditFilters.status||a.status===prototypeState.auditFilters.status)); let audits = prototypeState.audits.filter(a=>(!prototypeState.auditFilters.employee||a.employee===prototypeState.auditFilters.employee)&&(!prototypeState.auditFilters.status||a.status===prototypeState.auditFilters.status));
return `<div class="space-y-5"><div class="flex items-center justify-between rounded-2xl border border-slate-200 bg-white p-4 shadow-sm"><div><h3 class="font-semibold">自动化任务</h3><p class="text-sm text-slate-500">状态只使用 queued | running | succeeded | failed | canceled。</p></div>${actionBtn(icon('play')+'启动托管演示','data-modal-open="hosting-confirm"',true)}</div><div class="grid grid-cols-3 gap-4">${['运行中任务','历史任务','失败任务'].map((s,i)=>`<div class="rounded-2xl border bg-white p-4 shadow-sm"><h3 class="font-semibold">${s}</h3><p class="mt-2 text-2xl font-semibold">${i===0?prototypeState.tasks.filter(t=>t.status==='running').length:i===1?prototypeState.tasks.filter(t=>t.status==='succeeded').length:prototypeState.tasks.filter(t=>t.status==='failed').length}</p></div>`).join('')}</div>${table(['任务名','类型','状态','员工','目标应用','更新时间','操作'], rows)}<section class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"><button type="button" data-action="toggle-audit" class="flex h-10 w-full items-center justify-between font-semibold">审计日志 ${icon(prototypeState.auditOpen?'chevron-up':'chevron-down')}</button><div class="${prototypeState.auditOpen?'':'hidden'} mt-4 space-y-4"><div class="grid grid-cols-5 gap-3 text-sm"><select data-filter="time" class="h-10 rounded-xl border px-3"><option>时间</option></select><select data-filter="employee" class="h-10 rounded-xl border px-3"><option value="">员工</option><option>报销审核员</option><option>系统</option></select><select data-filter="app" class="h-10 rounded-xl border px-3"><option>应用</option></select><select data-filter="kb" class="h-10 rounded-xl border px-3"><option>知识库</option></select><select data-filter="status" class="h-10 rounded-xl border px-3"><option value="">任务状态</option><option>running</option><option>succeeded</option><option>failed</option><option>canceled</option></select></div>${table(['时间','员工','应用','知识库','状态','记录'], audits.map(a=>[a.time,a.employee,a.app,a.kb,`<span class="${clsBadge(a.status)}">${a.status}</span>`,a.text]))}</div></section></div>`; return `<div class="space-y-5"><div class="flex items-center justify-between rpa-panel p-4"><div><h3 class="font-semibold">自动化任务</h3><p class="text-sm text-[var(--rpa-muted)]">状态只使用 queued | running | succeeded | failed | canceled。</p></div>${actionBtn(icon('play')+'启动托管演示','data-modal-open="hosting-confirm"',true)}</div><div class="grid grid-cols-3 gap-4">${['运行中任务','历史任务','失败任务'].map((s,i)=>`<div class="rpa-panel p-4"><h3 class="font-semibold">${s}</h3><p class="mt-2 text-2xl font-semibold">${i===0?prototypeState.tasks.filter(t=>t.status==='running').length:i===1?prototypeState.tasks.filter(t=>t.status==='succeeded').length:prototypeState.tasks.filter(t=>t.status==='failed').length}</p></div>`).join('')}</div>${table(['任务名','类型','状态','员工','目标应用','更新时间','操作'], rows)}<section class="rpa-panel p-5"><button type="button" data-action="toggle-audit" class="flex h-10 w-full items-center justify-between font-semibold">审计日志 ${icon(prototypeState.auditOpen?'chevron-up':'chevron-down')}</button><div class="${prototypeState.auditOpen?'':'hidden'} mt-4 space-y-4"><div class="grid grid-cols-5 gap-3 text-sm"><select data-filter="time" class="h-10 rounded-xl border px-3"><option>时间</option></select><select data-filter="employee" class="h-10 rounded-xl border px-3"><option value="">员工</option><option>报销审核员</option><option>系统</option></select><select data-filter="app" class="h-10 rounded-xl border px-3"><option>应用</option></select><select data-filter="kb" class="h-10 rounded-xl border px-3"><option>知识库</option></select><select data-filter="status" class="h-10 rounded-xl border px-3"><option value="">任务状态</option><option>running</option><option>succeeded</option><option>failed</option><option>canceled</option></select></div>${table(['时间','员工','应用','知识库','状态','记录'], audits.map(a=>[a.time,a.employee,a.app,a.kb,`<span class="${clsBadge(a.status)}">${a.status}</span>`,a.text]))}</div></section></div>`;
} }
function settingsPage(){ function settingsPage(){
const groups = [ const groups = [
@ -194,9 +240,9 @@ function settingsPage(){
['诊断与日志',`${actionBtn('导出诊断包','data-action="diagnostic-full"')} ${actionBtn('查看本地日志目录','data-action="log-dir"')} ${actionBtn('复制环境信息','data-action="copy-env"')}`], ['诊断与日志',`${actionBtn('导出诊断包','data-action="diagnostic-full"')} ${actionBtn('查看本地日志目录','data-action="log-dir"')} ${actionBtn('复制环境信息','data-action="copy-env"')}`],
['数据备份与恢复','导出配置、导出知识库元数据、恢复 SQLite 与索引文件'], ['许可证管理','trial · 14 天试用 · 最多 3 个知识库 / 2 个 AI 员工 / 1 个标注应用 / 每日 20 次托管任务'] ['数据备份与恢复','导出配置、导出知识库元数据、恢复 SQLite 与索引文件'], ['许可证管理','trial · 14 天试用 · 最多 3 个知识库 / 2 个 AI 员工 / 1 个标注应用 / 每日 20 次托管任务']
]; ];
return `<div class="grid grid-cols-2 gap-4 pb-8">${groups.map(g=>`<section class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm"><h3 class="mb-3 font-semibold">${g[0]}</h3><div class="text-sm leading-7 text-slate-600">${g[1]}</div></section>`).join('')}</div>`; return `<div class="grid grid-cols-2 gap-4 pb-8">${groups.map(g=>`<section class="rpa-panel p-5"><h3 class="mb-3 font-semibold">${g[0]}</h3><div class="text-sm leading-7 text-[var(--rpa-muted)]">${g[1]}</div></section>`).join('')}</div>`;
} }
function table(headers, rows){ return `<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm"><table class="w-full text-left text-sm"><thead class="bg-slate-50 text-xs text-slate-500"><tr>${headers.map(h=>`<th class="px-4 py-3 font-medium">${h}</th>`).join('')}</tr></thead><tbody class="divide-y divide-slate-100">${rows.map(r=>`<tr class="align-top hover:bg-slate-50">${r.map(c=>`<td class="px-4 py-3">${c}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`; } function table(headers, rows){ return `<div class="rpa-table"><table class="w-full text-left text-sm"><thead class="text-xs"><tr>${headers.map(h=>`<th class="px-4 py-3 font-medium">${h}</th>`).join('')}</tr></thead><tbody>${rows.map(r=>`<tr class="align-top">${r.map(c=>`<td class="px-4 py-3">${c}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`; }
function renderOverlay(root){ function renderOverlay(root){
const layer = root.querySelector('[data-overlay-layer]'); const layer = root.querySelector('[data-overlay-layer]');
@ -207,34 +253,34 @@ function renderOverlay(root){
if (prototypeState.modal) { layer.innerHTML = modalHtml(prototypeState.modal); return; } if (prototypeState.modal) { layer.innerHTML = modalHtml(prototypeState.modal); return; }
layer.innerHTML = ''; layer.innerHTML = '';
} }
function modalFrame(title, body, wide=false){ return `<div class="flex h-full items-center justify-center bg-slate-950/40 p-8"><div class="max-h-full ${wide?'w-[760px]':'w-[560px]'} overflow-y-auto rounded-2xl bg-white p-6 shadow-2xl"><div class="mb-5 flex items-center justify-between"><h3 class="text-lg font-semibold">${title}</h3><button data-modal-close class="focus-ring grid h-9 w-9 place-items-center rounded-xl hover:bg-slate-100">${icon('x')}</button></div>${body}</div></div>`; } function modalFrame(title, body, wide=false){ return `<div class="rpa-scrim flex h-full items-center justify-center p-8"><div class="max-h-full ${wide?'w-[760px]':'w-[560px]'} overflow-y-auto rounded-2xl border p-6" style="background:var(--rpa-surface); border-color:var(--rpa-border); box-shadow:var(--rpa-shadow-blocking)"><div class="mb-5 flex items-center justify-between"><h3 class="text-lg font-semibold" style="color:var(--rpa-heading)">${title}</h3><button data-modal-close class="focus-ring grid h-9 w-9 place-items-center rounded-xl hover:bg-slate-100">${icon('x')}</button></div>${body}</div></div>`; }
function modalHtml(type){ function modalHtml(type){
if(type==='config') return configModal(); if(type==='config') return configModal();
if(type==='knowledge') return modalFrame('上传/创建知识库', `<form data-form="knowledge" class="space-y-4"><input name="name" required class="focus-ring h-10 w-full rounded-xl border px-3" placeholder="名称"><textarea name="description" class="focus-ring w-full rounded-xl border p-3" placeholder="描述"></textarea><select name="type" class="h-10 w-full rounded-xl border px-3"><option value="document">document</option><option value="image">image</option><option value="video">video</option><option value="folder">folder</option><option value="url">url</option></select><select name="permission" class="h-10 w-full rounded-xl border px-3"><option value="private">private</option><option value="selected_agents">selected_agents</option><option value="workspace">workspace</option></select><button class="h-10 rounded-xl bg-indigo-600 px-4 text-white">提交</button></form>`); if(type==='knowledge') return modalFrame('上传/创建知识库', `<form data-form="knowledge" class="space-y-4"><input name="name" required class="focus-ring h-10 w-full rounded-xl border px-3" placeholder="名称"><textarea name="description" class="focus-ring w-full rounded-xl border p-3" placeholder="描述"></textarea><select name="type" class="focus-ring h-10 w-full rounded-xl border px-3"><option value="document">document</option><option value="image">image</option><option value="video">video</option><option value="folder">folder</option><option value="url">url</option></select><select name="permission" class="focus-ring h-10 w-full rounded-xl border px-3"><option value="private">private</option><option value="selected_agents">selected_agents</option><option value="workspace">workspace</option></select><button class="focus-ring h-10 rounded-xl rpa-primary-btn px-4">提交</button></form>`);
if(type==='agent') return modalFrame('创建/编辑 AI 员工', `<form data-form="agent" class="space-y-4"><input name="role" required class="focus-ring h-10 w-full rounded-xl border px-3" placeholder="角色描述"><textarea name="prompt" class="focus-ring w-full rounded-xl border p-3" placeholder="系统提示词"></textarea><input class="h-10 w-full rounded-xl border px-3" placeholder="模型配置:本地模型"><input class="h-10 w-full rounded-xl border px-3" placeholder="知识库权限:财务报销制度"><input class="h-10 w-full rounded-xl border px-3" placeholder="可操作应用权限:企业报销系统"><button class="h-10 rounded-xl bg-indigo-600 px-4 text-white">提交</button></form>`); if(type==='agent') return modalFrame('创建/编辑 AI 员工', `<form data-form="agent" class="space-y-4"><input name="role" required class="focus-ring h-10 w-full rounded-xl border px-3" placeholder="角色描述"><textarea name="prompt" class="focus-ring w-full rounded-xl border p-3" placeholder="系统提示词"></textarea><input class="focus-ring h-10 w-full rounded-xl border px-3" placeholder="模型配置:本地模型"><input class="focus-ring h-10 w-full rounded-xl border px-3" placeholder="知识库权限:财务报销制度"><input class="focus-ring h-10 w-full rounded-xl border px-3" placeholder="可操作应用权限:企业报销系统"><button class="focus-ring h-10 rounded-xl rpa-primary-btn px-4">提交</button></form>`);
if(type==='permission') return modalFrame('权限模式', `<div class="space-y-3">${['private','selected_agents','workspace'].map(p=>`<button data-action="save-permission" data-value="${p}" class="focus-ring flex h-10 w-full items-center rounded-xl border px-3 text-sm hover:bg-slate-50">${p}</button>`).join('')}</div>`); if(type==='permission') return modalFrame('权限模式', `<div class="space-y-3">${['private','selected_agents','workspace'].map(p=>`<button data-action="save-permission" data-value="${p}" class="focus-ring flex h-10 w-full items-center rounded-xl border px-3 text-sm hover:bg-slate-50">${p}</button>`).join('')}</div>`);
if(type==='agent-confirm' || type==='hosting-confirm') return modalFrame('启动前确认', `<div class="space-y-4 text-sm"><div class="rounded-xl bg-indigo-50 p-4"><b>AI 员工</b><p>报销审核员</p></div><div class="rounded-xl bg-slate-50 p-4"><b>将访问的知识库</b><p>财务报销制度</p></div><div class="rounded-xl bg-slate-50 p-4"><b>将控制的应用</b><p>企业报销系统</p></div><div class="rounded-xl bg-slate-50 p-4"><b>预计动作范围</b><p>读取报销规则、填写金额、点击提交前暂停确认</p></div><div class="rounded-xl bg-slate-50 p-4"><b>停止方式</b><p>ESC 或遮罩上的停止托管按钮</p></div><div class="flex justify-end gap-3">${actionBtn('取消','data-modal-close')}${actionBtn('确认启动托管','data-action="start-hosting"',true)}</div></div>`); if(type==='agent-confirm' || type==='hosting-confirm') return modalFrame('启动前确认', `<div class="space-y-4 text-sm"><div class="rounded-xl p-4" style="background:var(--rpa-primary-soft)"><b>AI 员工</b><p>报销审核员</p></div><div class="rounded-xl p-4" style="background:var(--rpa-surface-subtle)"><b>将访问的知识库</b><p>财务报销制度</p></div><div class="rounded-xl p-4" style="background:var(--rpa-surface-subtle)"><b>将控制的应用</b><p>企业报销系统</p></div><div class="rounded-xl p-4" style="background:var(--rpa-surface-subtle)"><b>预计动作范围</b><p>读取报销规则、填写金额、点击提交前暂停确认</p></div><div class="rounded-xl p-4" style="background:var(--rpa-surface-subtle)"><b>停止方式</b><p>ESC 或遮罩上的停止托管按钮</p></div><div class="flex justify-end gap-3">${actionBtn('取消','data-modal-close')}${actionBtn('确认启动托管','data-action="start-hosting"',true)}</div></div>`);
return ''; return '';
} }
function configModal(){ function configModal(){
const steps=['权限检查','模型配置','性能评估','存储位置','安全说明']; const s=prototypeState.configStep; const steps=['权限检查','模型配置','性能评估','存储位置','安全说明']; const s=prototypeState.configStep;
const status=['通过','通过','通过','通过','需手动处理']; const status=['通过','通过','通过','通过','需手动处理'];
return modalFrame('首次配置与环境检查', `<div class="flex gap-5"><aside class="w-40 space-y-2">${steps.map((x,i)=>`<button data-action="config-step" data-index="${i}" class="h-10 w-full rounded-xl text-left text-sm ${s===i?'bg-indigo-600 px-3 text-white':'px-3 hover:bg-slate-100'}">${x}</button>`).join('')}</aside><div class="flex-1 space-y-4 text-sm"><p class="text-slate-500">状态枚举:未检查 | 检查中 | 通过 | 失败 | 需手动处理</p>${s===0?`<div class="space-y-2">${['屏幕录制','辅助功能/自动化控制','文件访问','网络访问'].map((x,i)=>`<div class="flex justify-between rounded-xl border p-3"><span>${x}</span><span class="${clsBadge(i===1?status[4]:'ready')}">${i===1?status[4]:'通过'}</span></div>`).join('')}</div><div class="rounded-xl bg-amber-50 p-4 text-amber-800">原因:辅助功能权限需手动授权;恢复:打开系统设置或重新检测。</div><div class="flex gap-2">${actionBtn('打开系统设置','data-action="open-settings"')}${actionBtn('重新检测','data-action="recheck-permission"',true)}${actionBtn('跳过并以后配置','data-modal-close')}</div>`:`<div class="rounded-xl border p-4"><b>${steps[s]}</b><p class="mt-2">当前状态:通过。本页用于演示 ${steps[s]} 配置检查。</p></div>`}</div></div>`, true); return modalFrame('首次配置与环境检查', `<div class="flex gap-5"><aside class="w-40 space-y-2">${steps.map((x,i)=>`<button data-action="config-step" data-index="${i}" class="focus-ring h-10 w-full rounded-xl text-left text-sm ${s===i?'rpa-primary-btn px-3':'px-3 hover:bg-slate-100'}">${x}</button>`).join('')}</aside><div class="flex-1 space-y-4 text-sm"><p style="color:var(--rpa-muted)">状态枚举:未检查 | 检查中 | 通过 | 失败 | 需手动处理</p>${s===0?`<div class="space-y-2">${['屏幕录制','辅助功能/自动化控制','文件访问','网络访问'].map((x,i)=>`<div class="flex justify-between rounded-xl border p-3"><span>${x}</span><span class="${clsBadge(i===1?status[4]:'ready')}">${i===1?status[4]:'通过'}</span></div>`).join('')}</div><div class="rounded-xl p-4" style="background:var(--rpa-warning-bg); color:#92400e">原因:辅助功能权限需手动授权;恢复:打开系统设置或重新检测。</div><div class="flex gap-2">${actionBtn('打开系统设置','data-action="open-settings"')}${actionBtn('重新检测','data-action="recheck-permission"',true)}${actionBtn('跳过并以后配置','data-modal-close')}</div>`:`<div class="rounded-xl border p-4"><b>${steps[s]}</b><p class="mt-2">当前状态:通过。本页用于演示 ${steps[s]} 配置检查。</p></div>`}</div></div>`, true);
} }
function annotationOverlay(){ function annotationOverlay(){
const b=prototypeState.draftBounds; const b=prototypeState.draftBounds;
return `<div class="relative h-full bg-slate-950/85 p-8 text-white"><div class="mx-auto flex h-full max-w-4xl flex-col gap-4"><div class="flex items-center justify-between"><div><h3 class="text-lg font-semibold">Ctrl+E 标注模式</h3><p class="text-sm text-slate-300">选择当前标注应用 → 框选窗口/节点 → 命名节点 → 保存</p></div>${actionBtn('退出','data-overlay-close')}</div><div class="grid grid-cols-[1fr_280px] gap-4 min-h-0 flex-1"><div class="relative overflow-hidden rounded-2xl bg-slate-200 p-5 text-slate-900"><div class="h-full rounded-xl bg-white p-5 shadow-inner"><div class="mb-4 flex h-10 items-center gap-2 rounded bg-slate-100 px-3"><b>企业报销系统</b><span class="text-sm text-slate-500">模拟桌面截图</span></div><div class="grid grid-cols-3 gap-4"><div class="h-80 rounded bg-slate-100"></div><div class="col-span-2 h-80 rounded bg-slate-50 p-4"><input class="mb-4 h-10 w-52 rounded border px-3" value="1280.00"><button class="absolute rounded bg-indigo-600 px-4 py-2 text-white" style="left:520px;top:430px">提交</button></div></div></div><div data-drag-box class="absolute cursor-move rounded-lg border-2 border-indigo-500 bg-indigo-500/20" style="left:${b.x}px;top:${b.y}px;width:${b.width}px;height:${b.height}px"></div></div><form data-form="node" class="space-y-4 rounded-2xl bg-white p-5 text-slate-900"><label class="block text-sm font-medium">当前标注应用<select class="mt-2 h-10 w-full rounded-xl border px-3"><option>企业报销系统</option></select></label><label class="block text-sm font-medium">节点名<input name="name" required class="mt-2 h-10 w-full rounded-xl border px-3" placeholder="测试提交按钮"></label><p class="rounded-xl bg-slate-50 p-3 text-xs">当前 bounds{ x: ${b.x}, y: ${b.y}, width: ${b.width}, height: ${b.height} }</p><button class="h-10 w-full rounded-xl bg-indigo-600 text-white">保存</button><button type="button" data-action="annotation-fail" class="h-9 text-sm text-red-600">模拟保存失败</button><button type="button" data-action="annotation-retry" class="hidden h-9 text-sm text-indigo-600">重试保存</button><p data-annotation-error class="hidden rounded-xl bg-red-50 p-3 text-sm text-red-700">保存失败:窗口句柄已变化,可保留草稿并重试。原因:窗口句柄已变化;恢复:保留草稿并重试保存。</p></form></div></div></div>`; return `<div class="rpa-hosting-scrim relative h-full p-8 text-white"><div class="mx-auto flex h-full max-w-4xl flex-col gap-4"><div class="flex items-center justify-between"><div><h3 class="text-lg font-semibold">Ctrl+E 标注模式</h3><p class="text-sm text-slate-300">选择当前标注应用 → 框选窗口/节点 → 命名节点 → 保存</p></div>${actionBtn('退出','data-overlay-close')}</div><div class="grid grid-cols-[1fr_280px] gap-4 min-h-0 flex-1"><div class="relative overflow-hidden rounded-2xl bg-slate-200 p-5 text-slate-900"><div class="h-full rounded-xl bg-white p-5 shadow-inner"><div class="mb-4 flex h-10 items-center gap-2 rounded bg-slate-100 px-3"><b>企业报销系统</b><span class="text-sm" style="color:var(--rpa-muted)">模拟桌面截图</span></div><div class="grid grid-cols-3 gap-4"><div class="h-80 rounded bg-slate-100"></div><div class="col-span-2 h-80 rounded bg-slate-50 p-4"><input class="mb-4 h-10 w-52 rounded border px-3" value="1280.00"><button class="absolute rounded rpa-primary-btn px-4 py-2" style="left:520px;top:430px">提交</button></div></div></div><div data-drag-box class="absolute cursor-move rounded-lg border-2" style="left:${b.x}px;top:${b.y}px;width:${b.width}px;height:${b.height}px; border-color:var(--rpa-primary); background:rgba(255,80,0,.16)"></div></div><form data-form="node" class="space-y-4 rounded-2xl bg-white p-5 text-slate-900"><label class="block text-sm font-medium">当前标注应用<select class="focus-ring mt-2 h-10 w-full rounded-xl border px-3"><option>企业报销系统</option></select></label><label class="block text-sm font-medium">节点名<input name="name" required class="focus-ring mt-2 h-10 w-full rounded-xl border px-3" placeholder="测试提交按钮"></label><p class="rpa-code p-3 text-xs">当前 bounds{ x: ${b.x}, y: ${b.y}, width: ${b.width}, height: ${b.height} }</p><button class="focus-ring h-10 w-full rounded-xl rpa-primary-btn">保存</button><button type="button" data-action="annotation-fail" class="h-9 text-sm rpa-link-btn" style="color:var(--rpa-danger)">模拟保存失败</button><button type="button" data-action="annotation-retry" class="hidden h-9 text-sm rpa-link-btn">重试保存</button><p data-annotation-error class="hidden rounded-xl p-3 text-sm" style="background:var(--rpa-danger-bg); color:var(--rpa-danger)">保存失败:窗口句柄已变化,可保留草稿并重试。原因:窗口句柄已变化;恢复:保留草稿并重试保存。</p></form></div></div></div>`;
} }
function hostingOverlay(){ function hostingOverlay(){
const actions=['读取报销规则','填写金额输入框','定位提交按钮','提交报销单']; const actions=['读取报销规则','填写金额输入框','定位提交按钮','提交报销单'];
const current=actions[Math.min(prototypeState.hostingStep, actions.length-1)]; const current=actions[Math.min(prototypeState.hostingStep, actions.length-1)];
return `<div class="flex h-full items-center justify-center bg-slate-950/90 p-8 text-white"><div class="w-[760px] rounded-3xl border border-white/10 bg-slate-900 p-6 shadow-2xl"><div class="flex items-center justify-between"><div><h3 class="text-xl font-semibold">RPA 托管执行中</h3><p class="text-sm text-slate-300">ESC 紧急停止始终可用</p></div><button data-action="stop-hosting" class="h-11 rounded-xl bg-red-600 px-5 font-semibold text-white">停止托管</button></div><div class="mt-5 rounded-2xl bg-slate-950 p-4"><p class="text-sm text-slate-400">当前动作</p><p class="mt-1 text-lg font-semibold">${current}</p><div class="mt-4 h-3 rounded-full bg-slate-800"><div class="h-3 rounded-full bg-indigo-500" style="width:${(prototypeState.hostingStep+1)*25}%"></div></div></div><div class="mt-5 max-h-48 overflow-y-auto rounded-2xl bg-slate-950 p-4 text-sm text-slate-300">${prototypeState.hostingLogs.map(l=>`<p>${l}</p>`).join('') || '<p>等待下一步动作。</p>'}</div><div class="mt-5 flex justify-end gap-3">${actionBtn('下一步动作','data-action="next-hosting"',true)}</div></div></div>`; return `<div class="flex h-full items-center justify-center p-8 text-white" style="background:rgba(10,9,30,.90)"><div class="w-[760px] rounded-3xl border p-6" style="background:var(--rpa-nav-panel); border-color:rgba(255,255,255,.10); box-shadow:var(--rpa-shadow-blocking)"><div class="flex items-center justify-between"><div><h3 class="text-xl font-semibold">RPA 托管执行中</h3><p class="text-sm text-slate-300">ESC 紧急停止始终可用</p></div><button data-action="stop-hosting" class="h-11 rounded-xl rpa-danger-btn px-5 font-semibold">停止托管</button></div><div class="mt-5 rounded-2xl p-4" style="background:#111827"><p class="text-sm text-slate-400">当前动作</p><p class="mt-1 text-lg font-semibold">${current}</p><div class="mt-4 h-3 rounded-full bg-slate-800"><div class="rpa-progress h-3 rounded-full" style="width:${(prototypeState.hostingStep+1)*25}%"></div></div></div><div class="mt-5 max-h-48 overflow-y-auto rounded-2xl p-4 text-sm text-slate-300" style="background:#080b14; font-family:var(--rpa-font-mono)">${prototypeState.hostingLogs.map(l=>`<p>${l}</p>`).join('') || '<p>等待下一步动作。</p>'}</div><div class="mt-5 flex justify-end gap-3">${actionBtn('下一步动作','data-action="next-hosting"',true)}</div></div></div>`;
} }
function highRiskOverlay(){ return `<div class="flex h-full items-center justify-center bg-slate-950/70 p-8"><div class="w-[520px] rounded-2xl bg-white p-6 shadow-2xl"><h3 class="text-lg font-semibold text-red-700">高风险动作:提交</h3><p class="mt-3 text-sm text-slate-600">确认后将向业务系统提交单据。</p><div class="mt-5 flex justify-end gap-3">${actionBtn('取消并等待人工','data-action="cancel-high-risk"')}${actionBtn('确认提交','data-action="confirm-high-risk"',true)}</div></div></div>`; } function highRiskOverlay(){ return `<div class="rpa-scrim flex h-full items-center justify-center p-8"><div class="w-[520px] rounded-2xl border p-6" style="background:var(--rpa-surface); border-color:var(--rpa-danger-border); box-shadow:var(--rpa-shadow-blocking)"><h3 class="text-lg font-semibold" style="color:var(--rpa-danger)">高风险动作:提交</h3><p class="mt-3 text-sm" style="color:var(--rpa-muted)">确认后将向业务系统提交单据。</p><div class="mt-5 flex justify-end gap-3">${actionBtn('取消并等待人工','data-action="cancel-high-risk"')}<button type="button" data-action="confirm-high-risk" class="focus-ring inline-flex h-10 items-center justify-center rounded-xl rpa-danger-btn px-4 text-sm font-medium">确认提交</button></div></div></div>`; }
document.addEventListener('click', event => { document.addEventListener('click', event => {
const platformTab = event.target.closest('[data-platform-tab]'); const platformTab = event.target.closest('[data-platform-tab]');
if(platformTab){ const target=platformTab.dataset.platformTab; prototypeState.activePlatform=target; document.querySelectorAll('[data-platform-panel]').forEach(p=>p.classList.toggle('hidden',p.dataset.platformPanel!==target)); document.querySelectorAll('[data-platform-tab]').forEach(b=>{b.className='focus-ring h-10 rounded-full px-5 text-sm font-medium text-slate-600';}); platformTab.className='focus-ring h-10 rounded-full bg-[var(--primary)] px-5 text-sm font-medium text-white'; return; } if(platformTab){ const target=platformTab.dataset.platformTab; prototypeState.activePlatform=target; document.querySelectorAll('[data-platform-panel]').forEach(p=>p.classList.toggle('hidden',p.dataset.platformPanel!==target)); document.querySelectorAll('[data-platform-tab]').forEach(b=>{b.className='focus-ring h-10 rounded-full px-5 text-sm font-medium text-[var(--rpa-muted)]';}); platformTab.className='focus-ring h-10 rounded-full bg-[var(--rpa-primary)] px-5 text-sm font-medium text-white'; return; }
const page = event.target.closest('[data-page]'); if(page){ prototypeState.page=page.dataset.page; renderAll(); return; } const page = event.target.closest('[data-page]'); if(page){ prototypeState.page=page.dataset.page; renderAll(); return; }
const tab = event.target.closest('[data-tab]'); if(tab){ prototypeState.activeAppTab=tab.dataset.tab; renderAll(); return; } const tab = event.target.closest('[data-tab]'); if(tab){ prototypeState.activeAppTab=tab.dataset.tab; renderAll(); return; }
const open = event.target.closest('[data-modal-open]'); if(open){ prototypeState.modal=open.dataset.modalOpen; renderAll(); return; } const open = event.target.closest('[data-modal-open]'); if(open){ prototypeState.modal=open.dataset.modalOpen; renderAll(); return; }