1107 lines
110 KiB
HTML
1107 lines
110 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>USER ERP 完整参考原型 v2 · 2026-05-28</title>
|
||
<style>
|
||
:root {
|
||
--bg:#f5f7fb; --panel:#fff; --panel2:#f8fafc; --text:#142033; --muted:#66758a;
|
||
--line:#dfe5ee; --line-s:#cbd5e1; --blue:#2563eb; --blue-s:#e8f0ff;
|
||
--green:#15803d; --green-s:#e8f7ee; --amber:#b45309; --amber-s:#fff4df;
|
||
--red:#b91c1c; --red-s:#fdecec; --purple:#6d28d9; --purple-s:#f2ecff;
|
||
--nav:#111827; --nav-s:#1f2937; --radius:8px; --shadow:0 8px 20px rgba(15,23,42,.04);
|
||
}
|
||
*{box-sizing:border-box}
|
||
body{margin:0;min-height:100vh;background:var(--bg);color:var(--text);font-family:"Microsoft YaHei","Segoe UI",Arial,sans-serif}
|
||
button,input,select{font:inherit}button{cursor:pointer}
|
||
|
||
/* App layout */
|
||
.app{display:grid;grid-template-columns:330px minmax(0,1fr);min-height:100vh}
|
||
@media(max-width:1120px){.app{grid-template-columns:1fr}}
|
||
|
||
/* SIDEBAR - dual rail */
|
||
.sidebar{height:100vh;position:sticky;top:0;display:grid;grid-template-columns:92px 238px;border-right:1px solid var(--line);background:var(--panel);z-index:10}
|
||
.rail{background:var(--nav);color:#e5edf7;display:grid;grid-template-rows:auto minmax(0,1fr) auto;gap:12px;padding:16px 12px;min-height:0}
|
||
.rail-brand{display:grid;gap:4px;justify-items:center;padding-bottom:14px;border-bottom:1px solid rgba(255,255,255,.12);text-align:center}
|
||
.rail-brand strong{font-size:14px;color:#fff}
|
||
.rail-brand span{color:#aebbc9;font-size:11px}
|
||
.rail-nav{display:grid;gap:8px;align-content:start;overflow:auto;min-height:0}
|
||
.rail-btn{min-height:66px;border:0;border-radius:var(--radius);background:transparent;color:#dbe7f5;display:grid;justify-items:center;align-content:center;gap:5px;padding:8px 4px;position:relative;font-size:12px}
|
||
.rail-btn:hover,.rail-btn.active{background:var(--nav-s);color:#fff}
|
||
.rail-btn.active::before{content:"";position:absolute;left:-12px;top:12px;bottom:12px;width:4px;background:var(--blue);border-radius:999px}
|
||
.rail-icon{width:28px;height:28px;border:1px solid rgba(255,255,255,.25);border-radius:7px;display:grid;place-items:center;font-size:12px;font-weight:800}
|
||
.rail-foot{border:1px solid rgba(255,255,255,.14);border-radius:var(--radius);padding:10px 6px;display:grid;gap:4px;text-align:center;color:#cbd5e1;font-size:11px}
|
||
|
||
.panel-nav{display:grid;grid-template-rows:auto minmax(0,1fr) auto;min-height:0;padding:18px 14px;gap:14px}
|
||
.module-head{padding-bottom:14px;border-bottom:1px solid var(--line);display:grid;gap:4px}
|
||
.module-head h1{margin:0;font-size:22px;line-height:1.2}
|
||
.module-head span{color:var(--muted);font-size:12px;line-height:1.5}
|
||
.eyebrow{margin:0;color:var(--muted);font-size:12px;font-weight:700}
|
||
.page-nav{overflow:auto;min-height:0;display:grid;gap:7px;align-content:start}
|
||
.page-btn,.quick-btn{border:1px solid transparent;background:transparent;color:var(--text);border-radius:var(--radius);min-height:42px;padding:9px 10px;display:flex;align-items:center;justify-content:space-between;gap:8px;text-align:left;font-size:13px}
|
||
.page-btn:hover,.quick-btn:hover,.page-btn.active{background:var(--blue-s);border-color:#bcd0ff;color:#123f9f}
|
||
.page-btn small{color:var(--muted);font-size:11px;flex:none}
|
||
.quick{border:1px solid var(--line);border-radius:var(--radius);padding:12px;background:var(--panel2);display:grid;gap:8px}
|
||
.quick-head{display:flex;justify-content:space-between;align-items:center;color:var(--muted);font-size:12px;font-weight:700}
|
||
|
||
/* MAIN */
|
||
.main{min-width:0;display:grid;grid-template-rows:auto minmax(0,1fr)}
|
||
.topbar{min-height:76px;position:sticky;top:0;z-index:8;background:rgba(245,247,251,.92);backdrop-filter:blur(12px);border-bottom:1px solid var(--line);padding:14px 24px;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
|
||
.top-title{display:grid;gap:4px}
|
||
.top-title strong{font-size:18px}
|
||
.top-title span{color:var(--muted);font-size:12px}
|
||
.top-actions{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:8px}
|
||
.search{height:38px;width:260px;display:flex;align-items:center;gap:8px;border:1px solid var(--line);border-radius:var(--radius);padding:0 10px;background:#fff;color:var(--muted);font-size:12px}
|
||
.search input{border:0;outline:none;width:100%;min-width:0;color:var(--text);background:transparent;font-size:13px}
|
||
select,input[type=date]{height:38px;border:1px solid var(--line);border-radius:var(--radius);background:#fff;color:var(--text);padding:0 10px;font-size:13px}
|
||
.period{height:38px;border:1px solid var(--line);border-radius:var(--radius);background:#fff;display:flex;overflow:hidden}
|
||
.period button{border:0;background:transparent;padding:0 12px;color:var(--muted);font-size:13px}
|
||
.period button.active{background:var(--blue);color:#fff}
|
||
.content{padding:24px;min-width:0}
|
||
|
||
/* Page header */
|
||
.page-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:16px;align-items:start;margin-bottom:18px}
|
||
.page-title{margin:0;font-size:26px;line-height:1.2}
|
||
.page-desc{margin:8px 0 0;max-width:900px;color:var(--muted);line-height:1.7;font-size:14px}
|
||
.head-actions{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}
|
||
.source-bar{border:1px solid #bfd2ff;background:#f4f8ff;color:#21406d;border-radius:var(--radius);padding:10px 12px;display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12px;line-height:1.5;margin-bottom:12px}
|
||
|
||
/* Cards & panels */
|
||
.grid{display:grid;gap:14px}
|
||
.cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}
|
||
.cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}
|
||
.cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}
|
||
@media(max-width:1120px){.cols-4,.cols-3,.cols-2{grid-template-columns:1fr}.page-head{grid-template-columns:1fr}.head-actions{justify-content:flex-start}}
|
||
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);min-width:0}
|
||
.panel-head{min-height:58px;border-bottom:1px solid var(--line);padding:14px 16px;display:flex;align-items:center;justify-content:space-between;gap:12px}
|
||
.panel-head h2{margin:0;font-size:16px;line-height:1.3}
|
||
.panel-head p,.panel-note{margin:4px 0 0;color:var(--muted);font-size:12px;line-height:1.5}
|
||
.panel-body{padding:16px}
|
||
|
||
/* Metric cards */
|
||
.metric{padding:16px;display:grid;gap:8px;min-height:116px}
|
||
.metric span{color:var(--muted);font-size:12px}
|
||
.metric strong{font-size:28px;line-height:1}
|
||
.metric p{margin:0;color:var(--muted);font-size:12px;line-height:1.5}
|
||
|
||
/* Tags */
|
||
.pill,.stage{display:inline-flex;align-items:center;min-height:24px;padding:3px 8px;border-radius:999px;font-size:12px;font-weight:700;white-space:nowrap}
|
||
.pill.blue,.stage.v1{color:#1d4ed8;background:var(--blue-s)}
|
||
.pill.green{color:var(--green);background:var(--green-s)}
|
||
.pill.amber,.stage.reserve{color:var(--amber);background:var(--amber-s)}
|
||
.pill.red{color:var(--red);background:var(--red-s)}
|
||
.pill.purple,.stage.v2{color:var(--purple);background:var(--purple-s)}
|
||
.pill.gray{color:var(--muted);background:#eef2f7}
|
||
.tag{display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600}
|
||
.tag-red{background:var(--red-s);color:var(--red)}
|
||
.tag-amber{background:var(--amber-s);color:var(--amber)}
|
||
.tag-green{background:var(--green-s);color:var(--green)}
|
||
.tag-blue{background:var(--blue-s);color:var(--blue)}
|
||
.tag-gray{background:#e5e7eb;color:#6b7280}
|
||
.tag-purple{background:var(--purple-s);color:var(--purple)}
|
||
.sim-watermark{display:inline-block;font-size:10px;color:#92400e;background:#fef3c7;padding:1px 6px;border-radius:3px;margin-left:4px;vertical-align:1px}
|
||
|
||
/* Flow visualization */
|
||
.flow{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
|
||
.flow-step{border:1px solid var(--line);background:#fff;border-radius:var(--radius);min-height:38px;padding:8px 10px;display:flex;align-items:center;gap:8px;color:var(--text);font-size:13px}
|
||
.flow-step::after{content:"";width:18px;height:1px;background:var(--line-s);display:inline-block}
|
||
.flow-step:last-child::after{display:none}
|
||
|
||
/* List items */
|
||
.list{display:grid;gap:10px;margin:0;padding:0;list-style:none}
|
||
.list li,.issue-row{border:1px solid var(--line);border-radius:var(--radius);background:#fff;padding:11px 12px;display:grid;gap:5px;min-width:0}
|
||
.list li strong,.issue-row strong{font-size:14px}
|
||
.list li span,.issue-row span{color:var(--muted);font-size:12px;line-height:1.55}
|
||
.task-row{border:1px solid var(--line);border-radius:var(--radius);background:#fff;padding:11px 12px;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:5px}
|
||
.task-row strong{font-size:14px}
|
||
.task-row span{color:var(--muted);font-size:12px;line-height:1.55}
|
||
|
||
/* Alert rows */
|
||
.alert-row{display:flex;align-items:center;gap:12px;padding:10px 14px;border-radius:6px;margin-bottom:8px;font-size:13px;border:1px solid var(--line);background:#fff}
|
||
.alert-row.p0{border-left:3px solid var(--red);background:var(--red-s)}
|
||
.alert-row.p1{border-left:3px solid var(--amber);background:var(--amber-s)}
|
||
.alert-row.p2{border-left:3px solid var(--blue);background:var(--blue-s)}
|
||
|
||
/* Cards */
|
||
.card{background:var(--panel);border-radius:var(--radius);border:1px solid var(--line);padding:18px 20px;margin-bottom:16px;box-shadow:0 2px 8px rgba(0,0,0,.03)}
|
||
.card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
|
||
.card-title{font-size:14px;font-weight:700}
|
||
.card-badge{font-size:11px;padding:2px 8px;border-radius:10px}
|
||
.card-badge.sim{background:#fef3c7;color:#92400e}
|
||
.card-badge.p0{background:var(--red-s);color:var(--red)}
|
||
.card-badge.p1{background:var(--amber-s);color:var(--amber)}
|
||
.card-badge.v1{background:var(--green-s);color:var(--green)}
|
||
.card-badge.v2{background:var(--purple-s);color:var(--purple)}
|
||
|
||
/* Stats */
|
||
.stats-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px;margin-bottom:16px}
|
||
.stat-card{background:var(--panel);border-radius:var(--radius);border:1px solid var(--line);padding:14px 16px}
|
||
.stat-card .stat-num{font-size:28px;font-weight:900;line-height:1.2}
|
||
.stat-card .stat-label{font-size:12px;color:var(--muted);margin-top:4px}
|
||
.stat-card.warn{border-left:3px solid var(--amber)}
|
||
.stat-card.danger{border-left:3px solid var(--red)}
|
||
.stat-card.good{border-left:3px solid var(--green)}
|
||
.stat-card.info{border-left:3px solid var(--blue)}
|
||
|
||
/* Tables */
|
||
table{width:100%;border-collapse:collapse;font-size:13px}
|
||
th,td{padding:11px 10px;border-bottom:1px solid var(--line);vertical-align:top;text-align:left}
|
||
th{color:var(--muted);font-size:12px;font-weight:800;background:#f8fafc}
|
||
td.actions-cell{min-width:150px;display:flex;gap:7px;flex-wrap:wrap}
|
||
.table-wrap{overflow:auto}
|
||
|
||
/* Buttons */
|
||
.btn{min-height:36px;border:1px solid var(--line-s);border-radius:var(--radius);background:#fff;color:var(--text);padding:0 12px;display:inline-flex;align-items:center;justify-content:center;gap:6px;font-size:13px;font-weight:700;text-decoration:none}
|
||
.btn:hover{border-color:#9db7ec;color:#1d4ed8;background:#f8fbff}
|
||
.btn.primary{border-color:var(--blue);background:var(--blue);color:#fff}
|
||
.btn.primary:hover{background:#1d4ed8}
|
||
.btn.danger{border-color:#f0b3b3;color:var(--red);background:var(--red-s)}
|
||
.btn.danger:hover{background:#f5c6c6}
|
||
.btn.small{min-height:30px;padding:0 9px;font-size:12px}
|
||
.btn.xs{padding:2px 8px;font-size:10px;min-height:24px}
|
||
|
||
/* Form */
|
||
.form-group{margin-bottom:12px}
|
||
.form-group label{display:block;font-size:12px;font-weight:600;color:var(--muted);margin-bottom:4px}
|
||
.form-group input,.form-group select,.form-group textarea{width:100%;padding:7px 10px;border:1px solid var(--line);border-radius:5px;font-size:13px}
|
||
.form-row{display:flex;gap:12px}
|
||
.form-row .form-group{flex:1}
|
||
|
||
/* Progress bar */
|
||
.progress-bar{height:6px;background:var(--line);border-radius:3px;overflow:hidden}
|
||
.progress-fill{height:100%;border-radius:3px}
|
||
.progress-fill.green{background:var(--green)}
|
||
.progress-fill.amber{background:var(--amber)}
|
||
.progress-fill.red{background:var(--red)}
|
||
.progress-fill.blue{background:var(--blue)}
|
||
.progress-fill.purple{background:var(--purple)}
|
||
|
||
/* Chat */
|
||
.chat-bubble{padding:8px 14px;border-radius:12px;margin:6px 0;max-width:70%;font-size:13px}
|
||
.chat-bubble.user{background:var(--blue-s);margin-right:auto}
|
||
.chat-bubble.agent{background:var(--panel2);margin-left:auto;border:1px solid var(--line)}
|
||
|
||
/* Drawer */
|
||
.drawer-mask{position:fixed;inset:0;background:rgba(15,23,42,.32);z-index:30;display:none}
|
||
.drawer-mask.open{display:block}
|
||
.drawer{position:fixed;top:0;right:0;width:min(640px,100vw);height:100vh;background:#fff;z-index:31;transform:translateX(100%);transition:transform .18s ease;display:grid;grid-template-rows:auto minmax(0,1fr) auto;border-left:1px solid var(--line)}
|
||
.drawer.open{transform:translateX(0)}
|
||
.drawer-head,.drawer-foot{padding:16px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;gap:12px}
|
||
.drawer-foot{border-top:1px solid var(--line);border-bottom:0;justify-content:flex-end}
|
||
.drawer-body{padding:16px;overflow:auto;display:grid;gap:12px}
|
||
.drawer h2{margin:0;font-size:18px}
|
||
.drawer p.panel-note{margin:4px 0 0;color:var(--muted);font-size:12px}
|
||
|
||
/* Toast */
|
||
.toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(18px);background:#111827;color:#fff;border-radius:var(--radius);padding:10px 14px;font-size:13px;opacity:0;pointer-events:none;transition:.18s ease;z-index:50}
|
||
.toast.open{opacity:1;transform:translateX(-50%) translateY(0)}
|
||
|
||
/* Spacer */
|
||
.spacer{height:14px}
|
||
|
||
/* Section title */
|
||
.section-title{font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin:20px 0 12px;padding-bottom:6px;border-bottom:1px solid var(--line)}
|
||
|
||
/* Role grid */
|
||
.role-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}
|
||
@media(max-width:1120px){.role-grid{grid-template-columns:1fr}}
|
||
.role-card{border:1px solid var(--line);border-radius:var(--radius);background:#fff;padding:12px;display:grid;gap:8px;min-height:128px;cursor:pointer}
|
||
.role-card:hover,.role-card.active{border-color:#94b5ff;box-shadow:inset 0 0 0 1px #94b5ff;background:#fbfdff}
|
||
.role-card strong{font-size:14px}
|
||
.role-card span{color:var(--muted);font-size:12px;line-height:1.55}
|
||
|
||
/* Filter bar */
|
||
.filter-bar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:16px}
|
||
.filter-bar input,.filter-bar select{padding:6px 10px;border:1px solid var(--line);border-radius:5px;font-size:12px;height:34px}
|
||
|
||
/* Tabs */
|
||
.tabs{display:flex;gap:0;border-bottom:1px solid var(--line);margin-bottom:16px}
|
||
.tab{padding:8px 18px;font-size:13px;border:none;background:none;cursor:pointer;border-bottom:2px solid transparent}
|
||
.tab.active{border-bottom-color:var(--blue);color:var(--blue);font-weight:600}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<!-- ====== SIDEBAR ====== -->
|
||
<aside class="sidebar">
|
||
<section class="rail" aria-label="一级模块">
|
||
<div class="rail-brand"><strong>USER ERP</strong><span>Stage 1 · v2</span></div>
|
||
<nav class="rail-nav" id="moduleRail"></nav>
|
||
<div class="rail-foot"><strong>模拟数据</strong><span>仅用于流程验证</span></div>
|
||
</section>
|
||
<section class="panel-nav">
|
||
<div class="module-head"><p class="eyebrow">当前模块</p><h1 id="moduleTitle">今日作战</h1><span id="moduleNote">从异常、资源和今日动作进入业务闭环。</span></div>
|
||
<nav class="page-nav" id="pageNav"></nav>
|
||
<div class="quick"><div class="quick-head"><span>高频跳转</span><strong>6</strong></div>
|
||
<button class="quick-btn" data-route="exceptions"><span>P0/P1异常</span><strong>37</strong></button>
|
||
<button class="quick-btn" data-route="demand-pool"><span>需求待评估</span><strong>8</strong></button>
|
||
<button class="quick-btn" data-route="ticket-pool"><span>客服超时工单</span><strong>3</strong></button>
|
||
<button class="quick-btn" data-route="free-plan"><span>免评缺口</span><strong>4</strong></button>
|
||
<button class="quick-btn" data-route="reviewer-list"><span>额度预警</span><strong>18</strong></button>
|
||
<button class="quick-btn" data-route="report-plan"><span>复盘待写</span><strong>7</strong></button>
|
||
</div>
|
||
</section>
|
||
</aside>
|
||
|
||
<!-- ====== MAIN ====== -->
|
||
<main class="main">
|
||
<header class="topbar">
|
||
<div class="top-title"><strong id="topPageTitle">今日作战台</strong><span id="topPageSubtitle">高级主管 / USER运营 / 客服 / 渠道 / KOC-KOL / 财务 / 风险</span></div>
|
||
<div class="top-actions">
|
||
<label class="search"><span>🔍</span><input id="globalSearch" placeholder="需求 / ASIN / 真实人 / 工单 / 风险" /></label>
|
||
<div class="period" id="period"><button class="active" data-period="day">日</button><button data-period="week">周</button><button data-period="month">月</button></div>
|
||
<input type="date" value="2026-05-28" />
|
||
<select id="roleSelect"><option value="director">高级主管</option><option value="ops">USER运营</option><option value="channel">渠道运营</option><option value="csLead">客服主管</option><option value="cs">一线客服</option><option value="koc">KOC/KOL运营</option><option value="finance">财务/返款</option><option value="risk">风险/审核</option></select>
|
||
<button class="btn" data-action="drawer" data-kind="source">来源 7</button>
|
||
</div>
|
||
</header>
|
||
<div class="content" id="content"></div>
|
||
</main>
|
||
</div>
|
||
|
||
<!-- ====== DRAWER ====== -->
|
||
<div class="drawer-mask" id="drawerMask"></div>
|
||
<aside class="drawer" id="drawer">
|
||
<div class="drawer-head"><div><h2 id="drawerTitle">详情</h2><p class="panel-note" id="drawerNote">模拟数据,敏感字段默认脱敏。</p></div><button class="btn" data-action="closeDrawer">关闭</button></div>
|
||
<div class="drawer-body" id="drawerBody"></div>
|
||
<div class="drawer-foot" id="drawerFoot"></div>
|
||
</aside>
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<script>
|
||
// ===== DATA =====
|
||
const sourceDocs = ["00_项目入口分级","01_主流程说明","02_日常操作页面结构","03_功能页面按钮盘点表","04_分支流程_完整需求域","05_异常流程_完整需求域","06_VibeCoding页面验证记录"];
|
||
|
||
const modules = [
|
||
{id:"overview",label:"作战",icon:"DB",title:"今日作战",note:"异常、目标、资源、角色工作流和复盘入口。",pages:["dashboard","role-workflow"]},
|
||
{id:"dispatch",label:"调度",icon:"PL",title:"需求与计划调度",note:"需求进入、计划生成、资源匹配、计划调整。",pages:["demand-pool","demand-review","plan-builder","exec-match","plan-adjust"]},
|
||
{id:"review",label:"评价",icon:"RV",title:"评价计划与订单",note:"测评/回评/免评计划、订单履约、评价核验。",pages:["review-plan","reply-plan","free-plan","review-order","reply-order"]},
|
||
{id:"csx",label:"客服",icon:"CS",title:"客服执行与管理",note:"工单池、聊天、答应配合、出勤、排班、绩效、目标。",pages:["ticket-pool","my-tickets","chat-view","promise-follow","reminder-pool","cs-dashboard","attendance","scheduling","cs-performance","cs-target"]},
|
||
{id:"channel",label:"渠道",icon:"CH",title:"渠道运营中心",note:"IM推送、卡片/H5、EDM、渠道漏斗。",pages:["im-push","im-card","edm-check","channel-funnel"]},
|
||
{id:"koc",label:"KOC",icon:"KC",title:"KOC/KOL协作中心",note:"线索池、达人档案、合作任务(V1预留)。",pages:["koc-leads","koc-profile","koc-task"]},
|
||
{id:"people",label:"测评人",icon:"PP",title:"测评人/真实人中心",note:"测评人列表、额度台账4/4/12、真实人归并。",pages:["reviewer-list","quota-ledger","identity-merge"]},
|
||
{id:"risk",label:"风险",icon:"RK",title:"风险与黑名单",note:"风险事件、黑名单、退款比对。",pages:["risk-events","blacklist","refund-compare"]},
|
||
{id:"finance",label:"财务",icon:"FN",title:"财务返款中心",note:"返款审核、返款记录。",pages:["refund-audit","refund-records"]},
|
||
{id:"report",label:"复盘",icon:"RP",title:"数据复盘与验证",note:"计划/ASIN/渠道/客服看板、异常总表、按钮矩阵、Vibe验证。",pages:["report-plan","report-asin","report-channel","report-cs","exceptions","action-matrix","validation"]}
|
||
];
|
||
|
||
// Page definitions
|
||
const PD = {};
|
||
function def(id, module, title, short, subtitle, owner, source, kind) {
|
||
PD[id] = {module,title,short:short||title,subtitle:subtitle||"",owner:owner||"",source:source||"",kind:kind||""};
|
||
}
|
||
def("dashboard","overview","今日作战台","作战台","所有角色每天打开后的第一屏:先看异常、目标缺口、资源压力和今日必须推进的动作。","高级主管 / USER运营负责人","00/01/02");
|
||
def("role-workflow","overview","角色每日工作流","角色工作流","明确每个岗位每天该看什么、不该先看什么、必须判断什么、复盘什么。","各岗位负责人","00/02/04");
|
||
def("demand-pool","dispatch","需求池","需求池","OA/销售/运营/ASIN异常/客服反馈/KOC-KOL需求统一入口。","USER运营","01/02/03/04","workflow");
|
||
def("demand-review","dispatch","需求评估","需求评估","校验ASIN、产品、库存、评分、目标、优先级。","USER运营","01/02/03","workflow");
|
||
def("plan-builder","dispatch","计划编排","计划编排","生成测评、回评、免评、客服、KOC/KOL计划。","USER运营","01/02/03","workflow");
|
||
def("exec-match","dispatch","执行匹配看板","执行匹配","人群、渠道、客服、KOC/KOL、风险、H5/卡片匹配。","USER运营","02/03","workflow");
|
||
def("plan-adjust","dispatch","计划调整","计划调整","补量、暂停、恢复、转免评、关闭、拆分/合并。","USER运营 / 主管","02/03","workflow");
|
||
def("review-plan","review","测评计划","测评计划","产品测评需求、计划周期、渠道、目标、进度。","USER运营","02/03");
|
||
def("reply-plan","review","回评计划","回评计划","掉评/差评/维稳/冲刺等回评需求与每日目标。","USER运营","02/03");
|
||
def("free-plan","review","免评计划","免评计划","长期测评人/KOC-KOL/补单免评计划。","USER运营 / KOC运营","02/03/04");
|
||
def("review-order","review","测评订单","测评订单","订单登记、上传回评、请款、返款、状态跟踪。","运营 / 客服","01/03/04","workflow");
|
||
def("reply-order","review","回评订单","回评订单","回评上传、确认、返款、售后来源、处理记录。","运营 / 审核","01/03/04","workflow");
|
||
def("ticket-pool","csx","工单池 · 客服执行中心","工单池","用户消息进入系统、工单生成、自动/手动分配。","一线客服 / 客服主管","02/03/04","workflow");
|
||
def("my-tickets","csx","我的工单","我的工单","当前客服今天处理什么:回复、登记订单、催评、上传结果。","一线客服","02/03/04","workflow");
|
||
def("chat-view","csx","聊天/消息","聊天消息","用户具体说了什么:快捷回复、补充信息、上下文查看。","一线客服","02/03");
|
||
def("promise-follow","csx","答应配合","答应配合","用户答应评价/反馈后是否完成:确认、拒绝、提醒、过期。","客服","02/03");
|
||
def("reminder-pool","csx","催评池","催评池","哪些测评/回评待催:发送提醒、转人工、关闭。","客服 / 运营","02/03");
|
||
def("cs-dashboard","csx","客服管理中心 · Dashboard","客服Dashboard","在线客服、今日工单、待处理、今日转化、本月目标。","客服主管","02/03");
|
||
def("attendance","csx","出勤管理","出勤管理","应出勤、实际出勤、出勤率、迟到/请假/缺勤。","客服主管","02/03");
|
||
def("scheduling","csx","排班管理","排班管理","早班、午班、晚班、渠道、最大工单数设置。","客服主管","02/03");
|
||
def("cs-performance","csx","转化绩效","转化绩效","RSO/RDO登记订单、获取评价、完成率、满意度。","客服主管","02/03");
|
||
def("cs-target","csx","目标管理","目标管理","月目标、历史完成、客服个人趋势设置与调整。","客服主管","02/03");
|
||
def("im-push","channel","IM推送","IM推送","用户分层、卡片、推送任务、回复、转化。","渠道运营","02/03/04","workflow");
|
||
def("im-card","channel","IM卡片/H5管理","IM卡片/H5","卡片、图片、链接、跳转、产品禁用联动。","渠道运营","02/03");
|
||
def("edm-check","channel","EDM每日检查","EDM检查","域名/邮箱/IP信誉、UID、H5、AB Test、转化。","EDM运营","02/03/04","workflow");
|
||
def("channel-funnel","channel","渠道漏斗看板","渠道漏斗","推送、曝光、点击、回复、订单、评价转化漏斗。","渠道负责人","02/03");
|
||
def("koc-leads","koc","KOC/KOL线索池","KOC线索池","新增线索、来源、标签、风险、分层(V1预留/V2实现)。","KOC/KOL运营","00/01/04/06","workflow");
|
||
def("koc-profile","koc","KOC/KOL达人档案","达人档案","国家、品类、内容能力、带货能力、合作状态(V1预留)。","KOC/KOL运营","00/01/04/06");
|
||
def("koc-task","koc","KOC/KOL合作任务","合作任务","样品、免评、内容、带货、佣金任务(V1预留/V2)。","KOC/KOL运营","00/01/04/06","workflow");
|
||
def("reviewer-list","people","测评人列表","测评人列表","编号、Joyhub、邮箱、电话、Profile、国家、标签(V1必做)。","USER运营","02/03");
|
||
def("quota-ledger","people","额度台账","额度台账","月度测评/免评/累计Review、预占/释放(V1必做)。","运营 / 客服主管 / 风险","02/03");
|
||
def("identity-merge","people","真实人归并","真实人归并","标准化姓名+地址、设备、邮箱等自动归并(V1必做/人工V1预留)。","主管 / 风险 / 管理员","02/03");
|
||
def("risk-events","risk","风险事件","风险事件","所有风险信号与人工复核案件(V1必做)。","风险/审核","02/03/05");
|
||
def("blacklist","risk","黑名单","黑名单","生效中/已移除、风险等级、来源、原因。","风险/审核","02/03/05");
|
||
def("refund-compare","risk","退款比对","退款比对","Amazon退款与OA返款双重比对(V1必做)。","风险 / 财务","03/05");
|
||
def("refund-audit","finance","返款审核","返款审核","付款前审核、确认返款、超额审核(V1必做)。","财务","03/04/05","workflow");
|
||
def("refund-records","finance","返款记录","返款记录","待请款、待审核、待返款、返款成功、锁定记录。","财务","03/04");
|
||
def("report-plan","report","计划看板","计划看板","总计划、进行中、完成率、缺口、补量复盘。","业务负责人","01/02");
|
||
def("report-asin","report","ASIN看板","ASIN看板","评分、Review、差评、掉评、库存、风险ASIN。","业务负责人","02");
|
||
def("report-channel","report","渠道看板","渠道看板","推送、曝光、点击、回复、转化、退订/投诉。","渠道负责人","02");
|
||
def("report-cs","report","客服看板","客服看板","工单、响应、解决、满意度、RSO/RDO转化。","客服主管","02");
|
||
def("exceptions","report","异常流程总表","异常总表","覆盖需求计划、评价订单、客服、渠道、KOC/KOL、返款、风险异常(P0-P3)。","风险 / 测试","05");
|
||
def("action-matrix","report","按钮行为矩阵","按钮矩阵","03按钮盘点表全部按钮:业务含义、读写对象、状态变化、权限审计。","产品 / 前端观察员","03");
|
||
def("validation","report","Vibe Coding页面验证记录","验证记录","现有原型只作为需求验证材料,不作为正式开发底座。","产品 / 前端观察员","06");
|
||
|
||
// Role definitions
|
||
const roles = {
|
||
director:{name:"高级主管",see:"OKR、资源压力、P0异常、跨部门阻塞、计划缺口",avoid:"不先看单条聊天、单条订单字段和普通P3观察项",judge:"是否暂停计划、是否补资源、是否升级跨部门",review:"目标缺口、TOP/BOTTOM任务、异常原因沉淀"},
|
||
ops:{name:"USER运营",see:"需求池、计划生成、执行匹配、今日缺口、待补量",avoid:"不把V1边界当需求边界,不只看订单页",judge:"需求是否可执行、资源是否匹配、是否转免评/客服/KOC",review:"计划完成、渠道表现、订单与评价口径"},
|
||
channel:{name:"渠道运营",see:"IM/EDM/Phone漏斗、人群、频控、卡片/H5、退订投诉",avoid:"不绕过真实人额度,不只按单渠道点击率决策",judge:"是否降频、换素材、换人群、暂停推送",review:"发送、点击、回复、订单、评价、投诉"},
|
||
csLead:{name:"客服主管",see:"在线、排班、待处理、首次回复、转化目标、投诉",avoid:"不先看已关闭低风险工单",judge:"是否自动分配、转移、调班、升级投诉",review:"RSO/RDO、首次回复、解决率、目标完成率"},
|
||
cs:{name:"一线客服",see:"我的工单、待回复、待催评、待登记订单、答应配合",avoid:"不看全局财务明细和他人敏感资料",judge:"回复、补信息、登记订单、催评、升级",review:"今日处理数、催评结果、用户承诺完成情况"},
|
||
koc:{name:"KOC/KOL运营",see:"新线索、标签、合作任务、内容/带货进度、身份冲突",avoid:"不把普通测评完成口径当内容任务完成口径",judge:"是否匹配达人、是否暂停合作、是否要求重提内容",review:"内容链接、Code、订单、佣金争议、风险"},
|
||
finance:{name:"财务/返款",see:"待请款、待审核、待返款、双重退款、凭证/卡密",avoid:"不看未到返款条件的普通工单",judge:"是否审核通过、是否阻断重复返款、是否补凭证",review:"返款成功率、异常返款、卡密缺失、争议"},
|
||
risk:{name:"风险/审核",see:"黑名单、强弱关联、退款取消、额度超限、敏感导出",avoid:"不只按账号判断,不用Profile累计Review替代真实人",judge:"弱风险提示、强风险阻断、拉黑/解除",review:"风险案件、复检记录、归并冲突、异常关闭"}
|
||
};
|
||
|
||
// Workflow data for per-branch pages
|
||
const WF = {
|
||
"demand-pool":{metrics:[["待评估需求","8","OA/销售/客服/KOC入口"],["资源匹配不足","5","人群或客服容量缺口"],["今日待转计划","6","测评/回评/免评/客服"],["冲突计划","2","需主管合并或暂停"]],do:["先看P0/P1需求和截止时间","校验ASIN、库存、评分、产品状态","确认需求类型:测评/回评/免评/客服/KOC","检查人群、额度、渠道、客服容量是否能支撑"],dont:["不直接从需求跳到推送","不在未核验产品/库存时生成计划","不把KOC/KOL合作请求当普通测评单处理"],steps:["需求进入","需求评估","产品/ASIN校验","计划生成","执行匹配","渠道/客服/KOC任务","结果回流"],issues:["目标高于可用人群/额度","产品禁用但计划仍执行","计划有名额但渠道无人群","渠道有响应但客服容量不足","完成数口径不一致"],actions:["新增需求","导入OA需求","需求评估","生成计划","匹配人群","预占额度","分配客服","生成推送任务","调整名额","暂停计划","恢复计划","转免评","关闭需求"],review:["需求转计划耗时","计划完成率","资源匹配缺口","客服容量压力","被驳回/需补充原因"]},
|
||
"review-order":{metrics:[["待登记订单","45","客服/用户提交"],["待确认评价","67","展示未确认"],["掉评/差评","9","触发回评"],["待请款","89","需财务审核"]],do:["先核验订单号、店铺、ASIN、订单状态","用户提交评价立即计入真实人累计12","Amazon展示确认才计计划完成","更换/更改/撤销必须留日志"],dont:["不把用户提交等同于展示完成","不对已退款/已取消订单继续返款","不把免评结果混进评价型完成数"],steps:["计划分配人群","生成/提交订单","核验订单号","绑定ASIN/店铺/站点","上传评价","展示核验","返款/请款","完成/异常关闭"],issues:["订单号格式错误","非公司产品","店铺下错","订单已取消/退款","评论重复绑定","评论未展示","掉评/差评"],actions:["新建测评计划","编辑计划","查看关联订单","新增订单","上传订单","上传回评","提交排队","请款","更换订单","更改订单","撤销","批量导出","回评确认"],review:["订单完成","评价提交","展示确认","返款成功","掉评率","异常关闭原因"]},
|
||
"ticket-pool":{metrics:[["待分配工单","19","含P1 6个"],["首次回复超时","7","需主管处理"],["今日登记订单","43","RSO/RDO"],["答应配合待完成","26","需催评"]],do:["先看未分配和超时工单","客服查看上下文卡后再回复","缺订单/截图/返款信息时创建待补标签","投诉、诈骗、退款异常立即升级"],dont:["不把客服降级成聊天窗口","不绕过订单核验直接请款","不查看无关敏感字段"],steps:["消息进入","生成工单","分配客服","查看上下文卡","回复/补信息","登记订单/催评/上传结果","关闭或升级"],issues:["无客服在线","工单未分配","首次回复超时","客服误登记订单","用户投诉客服"],actions:["自动分配","手动分配","转移工单","回复用户","登记订单","催评","标记解决","关闭工单","重开工单","确认答应","拒绝","标记过期"],review:["处理工单","首次回复","解决率","RSO/RDO转化","投诉","客服目标完成率"]},
|
||
"im-push":{metrics:[["可推人群","18,420","按真实人去重"],["长期测评人","1,204","累计提交评价>=12"],["回复率","4.8%","低于目标需换素材"],["额度预警","37","接近4/4/12"]],do:["先按真实人读取person_quota_ledgers","检查黑名单、退款取消、未完成单","确认卡片/H5和产品状态有效","查看今日频控和渠道去重"],dont:["不按Profile累计Review分层","不对累计>=12的真实人推普通测评/回评","不绕过客服容量继续放量"],steps:["人群生成","额度/风险复检","选择卡片/H5","推送","用户响应","核验信息","订单/返款/客服"],issues:["IM点击低","IM回复低于3%","退订或不感兴趣升高","卡片产品禁用未同步","H5链接失效"],actions:["新增推送","上架/下架","批量分配","新增卡片","下架卡片","配置去重规则"],review:["发送","曝光","点击","回复","订单","评价","退订/投诉"]},
|
||
"edm-check":{metrics:[["域名健康","2/3","1个预警"],["UID人群","9,880","今日可发"],["打开率","19.4%","模拟数据"],["投诉率","0.42%","接近阈值"]],do:["先看域名、邮箱、IP信誉","核对OA计划变化","检查KV/UID人群数量","确认H5与产品禁用联动"],dont:["不在信誉异常时继续发送","不把EDM点击当评价完成","不忽略菲律宾/国家问题清单"],steps:["基础设施检查","同步OA计划","检查UID人群","检查产品/H5","生成EDM任务","发送与AB Test","转化到订单/客服/风险"],issues:["EDM域名/IP信誉异常","KV/UID人群导出失败","H5链接失效","退订或不感兴趣升高"],actions:["检查基础设施","创建AB Test","生成推送任务","配置去重规则"],review:["送达","打开","点击","回复","退订","投诉","订单转化"]},
|
||
"koc-leads":{metrics:[["新线索","21","今日新增"],["合作任务","38","免评/内容/带货"],["内容待审核","12","V2"],["身份冲突","4","需复核"]],do:["先看新线索、任务超时、身份冲突","区分KOC/KOL身份和普通测评人身份","标签、带货、测评、免评不能混","内容任务与订单任务分开完成口径"],dont:["不因为V1不做完整商家平台就删除KOC/KOL需求","不把KOC内容任务混成普通测评订单"],steps:["新增线索","身份/标签/风险检查","自动打标与人工修正","分层","匹配任务","接受/拒绝","样品/内容/带货执行","复盘"],issues:["KOC标签混乱","达人重复触达","内容不合规","带货订单归因失败","高风险KOC继续推送"],actions:["新增线索","打标分层","创建合作任务","分配样品/免评名额","上传内容链接","审核内容","同步订单","计算佣金","暂停合作"],review:["线索","任务","内容","带货","佣金","风险","合作等级"]},
|
||
"risk-events":{metrics:[["风险事件","37","待处理"],["强关联命中","8","阻断"],["额度超限","18","真实人维度"],["敏感导出","2","需审批"]],do:["先看P0资金/合规/账号安全","每次有效互动复检身份、额度、风险","强风险阻断,弱风险提示人工","确认风险后同步黑名单"],dont:["不只按账号判断风险","不让黑名单用户继续触达/返款/合作","不导出未脱敏敏感字段"],steps:["风险信号产生","判断弱/强风险","弱风险提醒","强风险阻断","人工复核","解除/确认","黑名单同步/恢复执行"],issues:["黑名单命中","强关联命中","弱关联命中","额度超限","多账号归并冲突","频控超限","敏感字段导出风险"],actions:["创建风险事件","复核通过","排除风险","新增黑名单","标记双重退款","查看审计记录","手动合并","手动拆分"],review:["风险案件","黑名单","退款比对","额度超限","强弱关联","复检记录"]},
|
||
"refund-audit":{metrics:[["待审核请款","48","含P0 3"],["双重退款","5","需风险确认"],["卡密缺失","6","V1预留"],["佣金争议","3","V2实现"]],do:["先看待审核和P0资金风险","比对Amazon退款和OA返款","非APP邮件退款补齐识别线索","返款成功后回写订单和工单"],dont:["不审核未满足条件的请款","不跳过双重退款比对","不暴露完整返款账号和卡密"],steps:["返款条件满足","发起请款","财务审核","付款/礼品卡","凭证/卡密通知","状态回写","风险复盘"],issues:["缺返款账号","返款账号格式异常","重复请款","双重退款","非APP用户邮件退款","返款超额","卡密/凭证缺失"],actions:["请款","审核请款","确认返款","标记双重退款","创建风险事件"],review:["待请款","待审核","返款成功","异常返款","双重退款","凭证缺失"]}
|
||
};
|
||
|
||
// Exception groups (from 05)
|
||
const exceptionGroups = [
|
||
["需求与执行计划不匹配",[["需求目标高于可用人群/额度","提醒运营降低目标、拆分周期、转免评或补充渠道","V1必做"],["产品禁用但计划仍在执行","自动暂停计划和下架H5/卡片","V1必做"],["库存不足但继续推测评/免评","限制节奏或暂停","V1必做"],["ASIN/店铺/关键词变化未同步","阻断新推送,生成维护任务","V1必做"],["计划有名额但渠道无可推人群","补充人群、换渠道、延长周期","V1必做"],["渠道有响应但客服容量不足","暂停或降频推送,调整排班","V1必做"],["完成数口径不一致","进入口径复核","V1必做"]]],
|
||
["评价/订单异常",[["订单号格式错误","提示重填或转客服","V1必做"],["非公司产品","阻断登记,转客服说明","V1必做"],["店铺下错","人工处理,可转免评","V1必做"],["订单已取消/退款","阻断回评和返款,进入风险","V1必做"],["评论重复绑定","阻断或例外审核","V1必做"],["评论未展示","计划不计完成","V1必做"],["掉评/差评","触发回评/紧急催评/风险","V1必做"]]],
|
||
["返款/佣金异常",[["缺返款账号","客服跟进补齐","V1必做"],["重复请款","阻断并提醒财务","V1必做"],["双重退款","生成风险事件","V1必做"],["非APP用户邮件退款","补充识别线索","V1必做"],["KOC/KOL佣金争议","进入佣金复核","V2实现"]]],
|
||
["客服异常",[["无客服在线","提醒主管,暂停部分推送","V1必做"],["工单未分配","自动分配或主管手动分配","V1必做"],["首次回复超时","提醒客服/主管","V1必做"],["排班不足","调整排班或降频推送","V1必做"],["客服误登记订单","更改订单并留痕","V1必做"]]],
|
||
["渠道异常",[["IM点击低","换素材/卡片/人群","V1必做"],["IM回复低于3%","更换图片/话术","V1必做"],["EDM域名/IP信誉异常","暂停发送、切换账号","V1必做"],["KV/UID人群导出失败","重跑导出","V1必做"],["Phone未接率过高","调整时段/排班/回拨","V1预留"]]],
|
||
["KOC/KOL异常",[["KOC标签混乱","人工复核标签","V1预留"],["KOC/KOL与C类测评人身份重叠","阻断重复触达,进入冲突复核","V1必做"],["达人重复触达","合并或暂停任务","V1预留"],["内容不合规","驳回、下架、降权","V2实现"],["带货订单归因失败","进入人工归因","V2实现"]]],
|
||
["风险异常",[["黑名单命中","阻断触达/返款/合作","V1必做"],["强关联命中","阻断并复核","V1必做"],["额度超限","阻断普通测评,转免评池","V1必做"],["多账号归并冲突","按person_quota_ledgers重新汇总","V1必做"],["敏感字段导出风险","强制脱敏/审批","V1必做"]]]
|
||
];
|
||
|
||
// Action matrix rows (from 03)
|
||
const actionRows = [
|
||
["今日作战台","查看异常","进入昨日/今日异常详情","alert、plan、channel_event、ticket、risk_event","-","-","主管、运营","否","V1必做","exceptions"],
|
||
["今日作战台","指派处理人","把异常转成可执行任务","alert、staff、shift","task、notification","异常:待处理 -> 已指派","主管、运营","是","V1必做","exceptions"],
|
||
["今日作战台","升级异常","P0/P1问题升级主管或跨部门","alert、risk_event、ticket","escalation、notification","异常:待处理 -> 已升级","主管、风险","是","V1必做","exceptions"],
|
||
["今日作战台","调整计划","从作战台直接进入计划调整","demand、plan、progress","plan_change_log","计划:进行中 -> 调整中/暂停/补量","主管、运营","是","V1必做","plan-adjust"],
|
||
["今日作战台","创建复盘","对异常或任务形成复盘记录","plan、channel_event、ticket、order","retrospective","异常:已处理 -> 待复盘","主管、运营","是","V1预留","report-plan"],
|
||
["需求池","新增需求","手动录入销售/运营/KOC-KOL需求","product、asin、staff","demand","需求:草稿","运营、主管","是","V1必做","demand-pool"],
|
||
["需求池","导入OA需求","从OA或表格批量导入需求","import_file","demand、import_log","需求:待评估","运营、管理员","是","V1必做","demand-pool"],
|
||
["需求池","需求评估","判断需求是否可执行","demand、product、asin、inventory、risk","demand_review","待评估 -> 已通过/需补充/驳回","运营、主管","是","V1必做","demand-review"],
|
||
["需求池","生成计划","将需求转成测评/回评/免评/KOC-KOL/客服计划","demand、product、audience、quota、risk","plan、plan_log","已通过 -> 已转计划","运营、主管","是","V1必做","plan-builder"],
|
||
["执行匹配","匹配人群","生成候选用户/测评人/KOC-KOL名单","person、reviewer、koc_kol、quota、risk","audience_snapshot、exclusion","匹配:待匹配 -> 完成/不足","运营","是","V1必做","exec-match"],
|
||
["执行匹配","预占额度","锁定测评/免评/Review额度","audience_snapshot、quota_ledger","quota_reservation","额度:可用 -> 预占","运营","是","V1必做","quota-ledger"],
|
||
["执行匹配","分配客服","按容量、语言、工单量分配承接客服","plan、ticket、shift、agent_capacity","task、ticket_assignment","待分配 -> 已分配","主管、客服主管","是","V1必做","ticket-pool"],
|
||
["执行匹配","生成推送任务","从计划生成人群和渠道任务","plan、audience、card、h5、channel_config","push_task、channel_event","待发送","渠道运营","是","V1必做","im-push"],
|
||
["计划调整","调整名额","增减计划数量","plan、progress、resource","plan_change_log","计划目标变化","运营、主管","是","V1必做","plan-adjust"],
|
||
["计划调整","暂停计划","禁止继续执行但保留历史","plan、risk、product","plan_log、notification","进行中 -> 暂停","主管","是","V1必做","plan-adjust"],
|
||
["计划调整","恢复计划","恢复已暂停计划","plan、product、risk","plan_log","暂停 -> 进行中","主管","是","V1必做","plan-adjust"],
|
||
["计划调整","转免评","普通测评/回评因店铺/订单/用户问题转免评","plan、order、reviewer","plan、order_log","测评/回评 -> 免评","运营、主管","是","V1必做","free-plan"],
|
||
["测评订单","新增订单","记录测评执行单","plan、person、product","review_order","待上传回评/待登记","运营、客服","是","V1必做","review-order"],
|
||
["测评订单","上传订单","绑定Amazon订单号","review_order、amazon_order","review_order、audit_log","订单:待登记 -> 已登记","运营、客服","是","V1必做","review-order"],
|
||
["测评订单","上传回评","绑定评论ID/链接/截图","review_order、comment","review_submission","待回评 -> 待确认/已提交","运营、客服","是","V1必做","review-order"],
|
||
["测评订单","请款","发起用户返款","review_order、refund_account","refund_request","待请款 -> 待审核","运营、客服","是","V1必做","refund-audit"],
|
||
["测评订单","撤销","撤销错误/风险订单","review_order、risk","order_log、quota_release","进行中 -> 撤销","风险、主管","是","V1必做","review-order"],
|
||
["工单池","自动分配","按在线、工单量、排班分配客服","ticket、agent、shift","ticket_assignment","待分配 -> 待处理","客服主管","是","V1必做","ticket-pool"],
|
||
["工单池","手动分配","指定处理人","ticket、agent","ticket_assignment","待分配 -> 待处理","客服主管","是","V1必做","ticket-pool"],
|
||
["工单池","转移工单","换客服处理","ticket、agent","ticket_transfer_log","处理人变化","客服、主管","是","V1必做","ticket-pool"],
|
||
["我的工单","回复用户","处理用户消息","ticket、chat、context_snapshot","chat_message、ticket_log","待处理 -> 处理中/等待用户","客服","是","V1必做","my-tickets"],
|
||
["我的工单","登记订单","客服拿到订单号后登记","ticket、amazon_order、person","review_order、ticket_log","工单推进","客服","是","V1必做","my-tickets"],
|
||
["我的工单","催评","提醒用户提交评价","ticket、order、followup","reminder_event、followup","催评次数+1","客服、运营","是","V1必做","reminder-pool"],
|
||
["我的工单","标记解决","用户问题解决","ticket","ticket_log","处理中 -> 已解决","客服","是","V1必做","my-tickets"],
|
||
["我的工单","关闭工单","完成或无需继续","ticket","ticket_log","已解决/等待用户 -> 已关闭","客服、主管","是","V1必做","my-tickets"],
|
||
["答应配合","确认答应","用户答应评价/反馈","ticket、person","support_followup","待确认 -> 已确认","客服","是","V1必做","promise-follow"],
|
||
["排班管理","设置班次","给客服设置早/午/晚班","agent、date","shift_schedule","排班变化","客服主管","是","V1必做","scheduling"],
|
||
["目标管理","设置月目标","设置RSO/RDO登记和上评目标","agent、period","support_target","目标变化","客服主管","是","V1必做","cs-target"],
|
||
["IM推送","新增推送","创建IM触达任务","plan、audience、card","im_push_task","草稿/待发送","渠道运营","是","V1必做","im-push"],
|
||
["IM推送","上架/下架","控制任务是否可执行","im_push_task","channel_log","已上架/已下架","渠道运营","是","V1必做","im-push"],
|
||
["IM卡片","新增卡片","创建卡片素材","product、h5","im_card","已上架/草稿","渠道运营","是","V1必做","im-card"],
|
||
["EDM","检查基础设施","确认域名/邮箱/IP信誉","edm_config、deliverability","daily_check","健康/异常","EDM运营","否","V1必做","edm-check"],
|
||
["风险事件","创建风险事件","人工或系统发现风险","person、order、refund、creator","risk_case","待复核","风险、系统","是","V1必做","risk-events"],
|
||
["风险事件","复核通过","确认风险","risk_case","risk_case、blacklist","复核中 -> 确认风险","风险","是","V1必做","risk-events"],
|
||
["黑名单","新增黑名单","阻断后续触达/返款/合作","person、creator、identity","blacklist_item","生效中","风险","是","V1必做","blacklist"],
|
||
["退款比对","标记双重退款","Amazon退款+OA返款命中","refund_records","risk_signal","疑似/确认双重退款","风险、财务","是","V1必做","refund-compare"],
|
||
["返款","审核请款","付款前审核","refund_request、order、risk","refund_record","待审核 -> 待返款/失败","财务","是","V1必做","refund-audit"],
|
||
["返款","确认返款","完成付款/卡密发送","refund_record","refund_record、notification","待返款 -> 成功","财务","是","V1必做","refund-audit"]
|
||
].map((r,i)=>({id:`ACT-${String(i+1).padStart(3,"0")}`,page:r[0],name:r[1],meaning:r[2],read:r[3],write:r[4],change:r[5],role:r[6],audit:r[7],stage:r[8],route:r[9]}));
|
||
|
||
// ===== STATE =====
|
||
let state = {route:"dashboard",module:"overview",role:"director",period:"day"};
|
||
|
||
// ===== HELPERS =====
|
||
const $ = id => document.getElementById(id);
|
||
const esc = v => String(v??"").replace(/[&<>"']/g,ch=>({"&":"&","<":"<",">":">","\"":""","'":"'"}[ch]));
|
||
const S = () => '<span class="sim-watermark">模拟</span>';
|
||
const stageClass = s => s.includes("V2")?"v2":s.includes("预留")?"reserve":"v1";
|
||
function t(tag,c){return `<span class="tag tag-${c}">${tag}</span>`;}
|
||
function b(tag,c){return `<span class="pill ${c}">${tag}</span>`;}
|
||
function metric(label,value,note){return `<article class="panel metric"><span>${esc(label)}</span><strong>${esc(value)}</strong><p>${esc(note)} ${S()}</p></article>`;}
|
||
function infoBox(title,text,tone){return `<div class="issue-row"><span class="pill ${tone}">${esc(title)}</span><strong>${esc(text)}</strong></div>`;}
|
||
function flow(items){return `<div class="flow">${items.map(x=>`<span class="flow-step">${esc(x)}</span>`).join("")}</div>`;}
|
||
function sourceBar(){return `<div class="source-bar"><strong>需求来源</strong>${sourceDocs.map(d=>`<span class="pill gray">${esc(d)}</span>`).join("")}<span class="pill amber">页面数据均为模拟</span></div>`;}
|
||
function pageHeader(p,actions=""){return `<div class="page-head"><div>${sourceBar()}<div class="spacer"></div><h1 class="page-title">${esc(p.title)}</h1><p class="page-desc">${esc(p.subtitle)}</p></div><div class="head-actions">${actions}<button class="btn" data-action="drawer" data-kind="source">查看来源</button><button class="btn primary" data-route="action-matrix">按钮矩阵</button></div></div>`;}
|
||
|
||
function statCards(stats){return `<div class="stats-row">${stats.map(s=>`<div class="stat-card ${s.s||'info'}"><div class="stat-num">${s.n}</div><div class="stat-label">${s.l} ${S()}</div></div>`).join("")}</div>`;}
|
||
|
||
// ===== INIT =====
|
||
function init(){
|
||
renderShell();
|
||
const hash = location.hash.replace("#","");
|
||
setRoute(PD[hash]?hash:"dashboard",false);
|
||
bindEvents();
|
||
}
|
||
|
||
function renderShell(){
|
||
$("moduleRail").innerHTML = modules.map(m=>`
|
||
<button class="rail-btn" data-module="${m.id}" title="${esc(m.title)}"><span class="rail-icon">${esc(m.icon)}</span><span class="rail-label">${esc(m.label)}</span></button>
|
||
`).join("");
|
||
$("roleSelect").value = state.role;
|
||
}
|
||
|
||
function renderPageNav(moduleId){
|
||
const m = modules.find(x=>x.id===moduleId);
|
||
$("moduleTitle").textContent = m.title;
|
||
$("moduleNote").textContent = m.note;
|
||
$("pageNav").innerHTML = m.pages.map(id=>{
|
||
const p = PD[id];
|
||
return `<button class="page-btn${id===state.route?' active':''}" data-route="${id}"><span>${esc(p.short)}</span><small>${esc(p.source)}</small></button>`;
|
||
}).join("");
|
||
document.querySelectorAll(".rail-btn").forEach(b=>b.classList.toggle("active",b.dataset.module===moduleId));
|
||
}
|
||
|
||
function setRoute(route,push=true){
|
||
if(!PD[route]) route="dashboard";
|
||
state.route = route;
|
||
state.module = PD[route].module;
|
||
renderPageNav(state.module);
|
||
renderContent();
|
||
$("topPageTitle").textContent = PD[route].title;
|
||
$("topPageSubtitle").textContent = `${PD[route].owner} · 来源 ${PD[route].source} · ${state.period==="day"?"日视图":state.period==="week"?"周视图":"月视图"}`;
|
||
if(push) location.hash = route;
|
||
window.scrollTo({top:0,behavior:"smooth"});
|
||
}
|
||
|
||
// ===== CONTENT RENDERER =====
|
||
function renderContent(){
|
||
const r = state.route;
|
||
if(r==="dashboard") return renderDashboard();
|
||
if(r==="role-workflow") return renderRoleWorkflow();
|
||
if(r==="action-matrix") return renderActionMatrix();
|
||
if(r==="exceptions") return renderExceptions();
|
||
if(r==="validation") return renderValidation();
|
||
// Workflow pages with do/don't/flow/issues
|
||
if(WF[r]) return renderWorkflowPage(r);
|
||
// Regular data pages
|
||
return renderDataPage(r);
|
||
}
|
||
|
||
function renderDashboard(){
|
||
const role = roles[state.role];
|
||
$("content").innerHTML = `
|
||
${pageHeader(PD.dashboard,`<button class="btn primary" data-route="demand-pool">进入需求调度</button>`)}
|
||
<section class="grid cols-4">
|
||
${metric("P0/P1异常","37","直接影响今日目标、资金、合规或账号安全")}
|
||
${metric("今日必跟进","84","需求、计划、催评、返款、内容审核")}
|
||
${metric("资源压力","5类","人群、额度、客服、KOC/KOL、返款队列")}
|
||
${metric("待复盘","7","昨日TOP/BOTTOM和异常原因")}
|
||
</section>
|
||
<div class="spacer"></div>
|
||
<section class="grid cols-2">
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>当前角色:${esc(role.name)}</h2><p>切换右上角角色后,本卡片会更新每日工作重点。</p></div><span class="pill blue">角色视图</span></div>
|
||
<div class="panel-body grid cols-2">
|
||
${infoBox("每天先看",role.see,"green")}
|
||
${infoBox("今天不该先看",role.avoid,"amber")}
|
||
${infoBox("必须判断",role.judge,"blue")}
|
||
${infoBox("复盘数据",role.review,"purple")}
|
||
</div>
|
||
</article>
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>主业务闭环</h2><p>需求不是从推送开始,必须从需求进入走到复盘闭环。</p></div><button class="btn small" data-route="demand-pool">去调度</button></div>
|
||
<div class="panel-body">${flow(["需求进入","需求评估","产品校验","计划生成","执行匹配","渠道/客服/KOC任务","用户/达人响应","履约","风险复检","结果回流","复盘"])}</div>
|
||
</article>
|
||
</section>
|
||
<div class="spacer"></div>
|
||
<section class="grid cols-2">
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>P0/P1异常</h2></div><span class="pill red">需当天处理</span></div>
|
||
<div class="panel-body">
|
||
<div class="alert-row p0"><span style="font-weight:700;">P0</span> 产品 B0XXXXXX 库存低于安全线,3个测评计划受影响 <button class="btn small" data-route="plan-adjust">处理</button></div>
|
||
<div class="alert-row p0"><span style="font-weight:700;">P0</span> EDM域名信誉异常,暂停发送 <button class="btn small" data-route="edm-check">查看</button></div>
|
||
<div class="alert-row p1"><span style="font-weight:700;">P1</span> 客服工单超时未分配 3 件 <button class="btn small" data-route="ticket-pool">分配</button></div>
|
||
<div class="alert-row p1"><span style="font-weight:700;">P1</span> 测评计划 #RP-0528-003 完成率仅32% <button class="btn small" data-route="plan-adjust">调整</button></div>
|
||
<div class="alert-row p2"><span style="font-weight:700;">P2</span> IM回复率降至2.8%,低于3%阈值 <button class="btn small" data-route="channel-funnel">分析</button></div>
|
||
</div>
|
||
</article>
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>今日必须处理</h2><p>点击操作可跳转对应页面。</p></div><span class="pill red">模拟</span></div>
|
||
<div class="panel-body grid">
|
||
<div class="task-row"><div><strong>REQ-0528-001 · 测评 · 待评估</strong><span>B0USER001 / US · USER运营A · 评分低于4.5</span></div><div><span class="pill amber">P1</span> <button class="btn small" data-route="demand-review">进入</button></div></div>
|
||
<div class="task-row"><div><strong>PLAN-0528-006 · 免评 · 执行中</strong><span>B0USER019 / DE · KOC运营B · 身份冲突</span></div><div><span class="pill red">P0</span> <button class="btn small" data-route="free-plan">进入</button></div></div>
|
||
<div class="task-row"><div><strong>CS-0528-033 · 客服 · 首次回复超时</strong><span>B0USER011 / JP · 客服主管 · 待转移工单</span></div><div><span class="pill amber">P1</span> <button class="btn small" data-route="ticket-pool">进入</button></div></div>
|
||
<div class="task-row"><div><strong>RISK-0528-009 · 风险 · 强关联命中</strong><span>真实人 P-8842 · 风险审核 · 多账号归并额度达到12</span></div><div><span class="pill red">P0</span> <button class="btn small" data-route="risk-events">进入</button></div></div>
|
||
</div>
|
||
</article>
|
||
</section>
|
||
<div class="spacer"></div>
|
||
<section class="panel">
|
||
<div class="panel-head"><div><h2>主看板确认项</h2><p>交付前确认页面是否照顾每日工作流。</p></div><span class="pill green">已覆盖</span></div>
|
||
<div class="panel-body">
|
||
<ul class="list">
|
||
<li><strong>主看板</strong><span>P0/P1异常、今日任务、目标进度、资源压力、复盘入口都在首屏。</span></li>
|
||
<li><strong>分支看板</strong><span>IM、EDM、Phone、客服、评价订单、免评、财务、KOC/KOL、风险均有独立页面。</span></li>
|
||
<li><strong>按钮跳转</strong><span>03按钮盘点表已进入按钮行为矩阵,每个动作均可点击。</span></li>
|
||
<li><strong>口径提醒</strong><span>真实人、4/4/12、提交即计12、展示才计完成、免评独立结果口径均显式展示。</span></li>
|
||
<li><strong>模拟数据</strong><span>所有数据均标注为模拟,只用于本地点击和需求验证。</span></li>
|
||
</ul>
|
||
</div>
|
||
</section>`;
|
||
}
|
||
|
||
function renderRoleWorkflow(){
|
||
$("content").innerHTML = `
|
||
${pageHeader(PD["role-workflow"])}
|
||
<section class="role-grid">
|
||
${Object.entries(roles).map(([id,r])=>`
|
||
<button class="role-card ${state.role===id?"active":""}" data-role-card="${id}"><strong>${esc(r.name)}</strong><span>先看:${esc(r.see)}</span><span>不先看:${esc(r.avoid)}</span></button>
|
||
`).join("")}
|
||
</section>
|
||
<div class="spacer"></div>
|
||
<section class="panel">
|
||
<div class="panel-head"><div><h2>角色工作流要求</h2><p>用于确认工作人员每天进入系统时的优先级。</p></div><span class="pill blue">Stage 1</span></div>
|
||
<div class="panel-body table-wrap">
|
||
<table><thead><tr><th>角色</th><th>每天先看</th><th>今天不该先看</th><th>必须判断</th><th>复盘数据</th><th>入口</th></tr></thead>
|
||
<tbody>${Object.entries(roles).map(([id,r])=>`<tr><td><strong>${esc(r.name)}</strong></td><td>${esc(r.see)}</td><td>${esc(r.avoid)}</td><td>${esc(r.judge)}</td><td>${esc(r.review)}</td><td><button class="btn small" data-route="${id==="cs"?"ticket-pool":id==="koc"?"koc-leads":id==="finance"?"refund-audit":id==="risk"?"risk-events":id==="channel"?"im-push":"dashboard"}">进入</button></td></tr>`).join("")}</tbody></table>
|
||
</div>
|
||
</section>`;
|
||
}
|
||
|
||
function renderWorkflowPage(route){
|
||
const p = PD[route];
|
||
const data = WF[route] || WF["demand-pool"];
|
||
$("content").innerHTML = `
|
||
${pageHeader(p,`<button class="btn primary" data-route="exceptions">看异常</button>`)}
|
||
<section class="grid cols-4">${data.metrics.map(m=>metric(m[0],m[1],m[2])).join("")}</section>
|
||
<div class="spacer"></div>
|
||
<section class="grid cols-2">
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>工作人员每天该看什么</h2><p>先看会影响今日目标或阻断流程的项。</p></div><span class="pill green">先看</span></div>
|
||
<div class="panel-body"><ul class="list">${data.do.map(x=>`<li><strong>${esc(x)}</strong><span>模拟检查项,可点击右侧动作继续。</span></li>`).join("")}</ul></div>
|
||
</article>
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>今天不该先看什么</h2><p>避免低价值页面占用操作时间或制造错误口径。</p></div><span class="pill amber">不要先看</span></div>
|
||
<div class="panel-body"><ul class="list">${data.dont.map(x=>`<li><strong>${esc(x)}</strong><span>必要时由异常、工单或复盘入口进入。</span></li>`).join("")}</ul></div>
|
||
</article>
|
||
</section>
|
||
<div class="spacer"></div>
|
||
<section class="panel">
|
||
<div class="panel-head"><div><h2>流程点击路径</h2><p>按业务顺序模拟从入口到关闭或复盘。</p></div><button class="btn small" data-action="drawer" data-kind="flow" data-route-key="${route}">查看写入对象</button></div>
|
||
<div class="panel-body">${flow(data.steps)}</div>
|
||
</section>
|
||
<div class="spacer"></div>
|
||
<section class="grid cols-2">
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>流程问题点</h2><p>来自05异常流程,点击处理会打开演示抽屉。</p></div><span class="pill red">异常</span></div>
|
||
<div class="panel-body grid">${data.issues.map(x=>`<div class="issue-row"><strong>${esc(x)}</strong><span>处理状态:待处理 -> 处理中 -> 待复核 -> 已关闭</span><button class="btn small" data-action="drawer" data-kind="issue" data-title="${esc(x)}" data-route-target="exceptions">处理演示</button></div>`).join("")}</div>
|
||
</article>
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>可执行按钮</h2><p>按钮来自03盘点表,可跳转或执行演示。</p></div><span class="pill blue">${data.actions.length} 项</span></div>
|
||
<div class="panel-body" style="display:flex;flex-wrap:wrap;gap:8px">${data.actions.map(name=>{
|
||
const a = actionRows.find(x=>x.name===name);
|
||
return a?`<button class="btn small" data-action-id="${a.id}">${esc(name)}</button>`:`<button class="btn small" data-action="drawer" data-kind="generic" data-title="${esc(name)}">${esc(name)}</button>`;
|
||
}).join("")}</div>
|
||
</article>
|
||
</section>
|
||
<div class="spacer"></div>
|
||
<section class="panel">
|
||
<div class="panel-head"><div><h2>将来复盘数据</h2><p>用于下一轮计划、资源、渠道和人员调度。</p></div><button class="btn small" data-route="report-plan">进入复盘</button></div>
|
||
<div class="panel-body">${data.review.map(x=>`<span class="pill gray" style="margin:0 6px 8px 0">${esc(x)}</span>`).join("")}</div>
|
||
</section>`;
|
||
}
|
||
|
||
function renderActionMatrix(){
|
||
$("content").innerHTML = `
|
||
${pageHeader(PD["action-matrix"],`<button class="btn primary" data-route="dashboard">返回主看板</button>`)}
|
||
<section class="grid cols-4">
|
||
${metric("按钮动作",actionRows.length,"全部可点击")}
|
||
${metric("V1必做",actionRows.filter(a=>a.stage.includes("V1必做")).length,"第一版关键动作")}
|
||
${metric("V1预留",actionRows.filter(a=>a.stage.includes("预留")).length,"保留入口/字段/状态")}
|
||
${metric("V2实现",actionRows.filter(a=>a.stage.includes("V2")).length,"后续完整能力")}
|
||
</section>
|
||
<div class="spacer"></div>
|
||
<section class="panel">
|
||
<div class="panel-head"><div><h2>03 功能页面按钮盘点表</h2><p>每行提供执行演示和页面跳转。点击后会打开抽屉或跳转,不存在死按钮。</p></div><span class="pill amber">模拟写入</span></div>
|
||
<div class="panel-body table-wrap">
|
||
<table><thead><tr><th>ID</th><th>页面</th><th>按钮/动作</th><th>业务含义</th><th>读/写对象</th><th>状态变化</th><th>权限</th><th>阶段</th><th>操作</th></tr></thead>
|
||
<tbody>${actionRows.map(a=>`<tr><td>${a.id}</td><td>${esc(a.page)}</td><td><strong>${esc(a.name)}</strong></td><td>${esc(a.meaning)}</td><td><span class="pill gray">读</span> ${esc(a.read)}<br><span class="pill gray">写</span> ${esc(a.write)}</td><td>${esc(a.change)}</td><td>${esc(a.role)}<br>审计:${esc(a.audit)}</td><td><span class="stage ${stageClass(a.stage)}">${esc(a.stage)}</span></td><td class="actions-cell"><button class="btn small" data-action-id="${a.id}">执行演示</button><button class="btn small" data-route="${a.route}">跳转页面</button></td></tr>`).join("")}</tbody></table>
|
||
</div>
|
||
</section>`;
|
||
}
|
||
|
||
function renderExceptions(){
|
||
$("content").innerHTML = `
|
||
${pageHeader(PD.exceptions,`<button class="btn primary" data-route="dashboard">返回作战台</button>`)}
|
||
<section class="grid cols-4">
|
||
${metric("P0","9","当天处理,主管可见")}
|
||
${metric("P1","18","24小时内处理")}
|
||
${metric("P2","31","责任人跟进")}
|
||
${metric("P3","12","观察项")}
|
||
</section>
|
||
<div class="spacer"></div>
|
||
<section class="grid">
|
||
${exceptionGroups.map(g=>`
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>${esc(g[0])}</h2><p>发现方式、处理动作、关闭条件后续进入测试用例。</p></div><span class="pill red">${g[1].length}</span></div>
|
||
<div class="panel-body table-wrap">
|
||
<table><thead><tr><th>异常</th><th>处理</th><th>阶段</th><th>操作</th></tr></thead>
|
||
<tbody>${g[1].map(row=>`<tr><td><strong>${esc(row[0])}</strong></td><td>${esc(row[1])}</td><td><span class="stage ${stageClass(row[2])}">${esc(row[2])}</span></td><td><button class="btn small" data-action="drawer" data-kind="issue" data-title="${esc(row[0])}" data-route-target="exceptions">处理演示</button></td></tr>`).join("")}</tbody></table>
|
||
</div>
|
||
</article>
|
||
`).join("")}
|
||
</section>`;
|
||
}
|
||
|
||
function renderValidation(){
|
||
$("content").innerHTML = `
|
||
${pageHeader(PD.validation)}
|
||
<section class="grid cols-2">
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>已参考原型</h2><p>原型是需求验证材料,不是生产底座。</p></div><span class="pill blue">06</span></div>
|
||
<div class="panel-body"><ul class="list">
|
||
<li><strong>C:\\XCODE\\USER\\input\\user-review-system</strong><span>React/Vite原型,参考菜单、路由、订单、客服、渠道、风险页面。</span></li>
|
||
<li><strong>C:\\XCODE\\USER\\src\\user_erp_mvp_admin_prototype_v10.html</strong><span>参考左侧模块栏、顶部角色/周期、抽屉/弹窗后台布局。</span></li>
|
||
<li><strong>C:\\XCODE\\USER\\input\\客服执行.html</strong><span>参考客服执行、分配、排班、绩效、目标管理。</span></li>
|
||
<li><strong>C:\\XCODE\\USER\\input\\amazon_operator_test_entry.html</strong><span>参考提评入口、IM账号、推送策略。</span></li>
|
||
<li><strong>C:\\XCODE\\USER\\input\\IM 推送业务流.mm</strong><span>参考IM分层和流程,已修正为真实人口径。</span></li>
|
||
</ul></div>
|
||
</article>
|
||
<article class="panel">
|
||
<div class="panel-head"><div><h2>本原型补强方向</h2><p>覆盖现有原型缺口。</p></div><span class="pill green">已补</span></div>
|
||
<div class="panel-body"><ul class="list">
|
||
<li><strong>需求与计划调度</strong><span>从需求池到计划、资源、人群、客服、渠道、KOC/KOL任务。</span></li>
|
||
<li><strong>客服核心需求</strong><span>客服执行和客服管理均有页面、动作、异常与复盘指标。</span></li>
|
||
<li><strong>KOC/KOL完整需求域</strong><span>线索、分层、任务、内容、带货、佣金、风险均进入页面。</span></li>
|
||
<li><strong>免评独立闭环</strong><span>免评不再和评价提交混为同一终点。</span></li>
|
||
<li><strong>按钮矩阵</strong><span>03中动作全部以可点击入口呈现。</span></li>
|
||
</ul></div>
|
||
</article>
|
||
</section>`;
|
||
}
|
||
|
||
// ===== DATA PAGES (simple data tables) =====
|
||
function renderDataPage(route){
|
||
const p = PD[route];
|
||
let html = pageHeader(p) + '<div class="spacer"></div>';
|
||
switch(route){
|
||
|
||
case "demand-pool":
|
||
html += statCards([{n:"8",l:"待评估",s:"warn"},{n:"3",l:"需补充",s:"info"},{n:"15",l:"已转计划",s:"good"},{n:"2",l:"已驳回",s:"danger"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>需求列表</h2><div style="display:flex;gap:8px"><button class="btn primary small" data-action="drawer" data-kind="generic" data-title="新增需求">+ 新增需求</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="导入OA需求">📥 导入OA</button></div></div><div class="panel-body table-wrap">
|
||
<table><tr><th>需求编号</th><th>来源</th><th>类型</th><th>产品/ASIN</th><th>站点</th><th>优先级</th><th>状态</th><th>操作</th></tr>
|
||
<tr><td>D-0528-001</td><td>OA系统</td><td>${t("测评","blue")}</td><td>B0XXXXXX01</td><td>US</td><td>${t("S","red")}</td><td>${t("待评估","amber")}</td><td><button class="btn small" data-route="demand-review">评估</button><button class="btn small" data-route="plan-builder">转计划</button></td></tr>
|
||
<tr><td>D-0528-002</td><td>销售提报</td><td>${t("回评","amber")}</td><td>B0XXXXXX02</td><td>UK</td><td>${t("A","amber")}</td><td>${t("待评估","amber")}</td><td><button class="btn small" data-route="demand-review">评估</button></td></tr>
|
||
<tr><td>D-0528-003</td><td>ASIN异常</td><td>${t("紧急催评","red")}</td><td>B0XXXXXX03</td><td>US</td><td>${t("S","red")}</td><td>${t("需补充","amber")}</td><td><button class="btn small" data-route="demand-review">补充信息</button></td></tr>
|
||
<tr><td>D-0527-005</td><td>运营手动</td><td>${t("免评","purple")}</td><td>B0XXXXXX04</td><td>DE</td><td>${t("B","blue")}</td><td>${t("已通过","green")}</td><td><button class="btn small" data-route="plan-builder">生成计划</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "demand-review":
|
||
html += `<div class="grid cols-2">
|
||
<div class="panel"><div class="panel-head"><h2>需求信息 · D-0528-001</h2></div><div class="panel-body"><table>
|
||
<tr><th style="width:120px">需求编号</th><td>D-0528-001</td></tr><tr><th>来源</th><td>OA系统</td></tr><tr><th>类型</th><td>测评</td></tr><tr><th>产品/ASIN</th><td>B0XXXXXX01 · 智能玩具</td></tr><tr><th>站点/店铺</th><td>US · 主店铺</td></tr><tr><th>目标</th><td>Review +30,评分目标 4.3+</td></tr><tr><th>优先级</th><td>${t("S","red")}</td></tr><tr><th>截止</th><td>2026-06-10</td></tr></table></div></div>
|
||
<div class="panel"><div class="panel-head"><h2>产品/ASIN校验</h2></div><div class="panel-body"><table>
|
||
<tr><th>校验项</th><th>结果</th></tr><tr><td>产品启用</td><td>${t("正常","green")}</td></tr><tr><td>ASIN有效性</td><td>${t("正确","green")}</td></tr><tr><td>库存</td><td>${t("紧张(200)","amber")}</td></tr><tr><td>评分</td><td>4.1 (86 Review)</td></tr><tr><td>近7天掉评</td><td>${t("2条","amber")}</td></tr><tr><td>H5/卡片</td><td>${t("缺H5","amber")}</td></tr></table></div></div></div>
|
||
<div class="panel"><div class="panel-head"><h2>评估决策</h2></div><div class="panel-body" style="display:flex;gap:10px">
|
||
<button class="btn primary" data-action="drawer" data-kind="generic" data-title="评估通过">✅ 评估通过,生成计划</button>
|
||
<button class="btn" data-action="drawer" data-kind="generic" data-title="退回补充">↩ 退回补充</button>
|
||
<button class="btn" data-action="drawer" data-kind="generic" data-title="暂缓">⏸ 暂缓</button>
|
||
<button class="btn danger" data-action="drawer" data-kind="generic" data-title="驳回">✕ 驳回</button></div></div>`;
|
||
break;
|
||
|
||
case "plan-builder":
|
||
html += `<div class="panel"><div class="panel-head"><h2>从需求生成计划</h2><span class="pill v1">V1必做</span></div><div class="panel-body">
|
||
<div class="form-row"><div class="form-group"><label>关联需求</label><select><option>D-0528-001 · 测评 · B0XXXXXX01</option></select></div>
|
||
<div class="form-group"><label>计划类型</label><select><option>测评计划</option><option>回评计划</option><option>免评计划</option></select></div></div>
|
||
<div class="form-row"><div class="form-group"><label>目标数量</label><input type="number" value="30"/></div>
|
||
<div class="form-group"><label>周期</label><input type="date" value="2026-05-28"/> ~ <input type="date" value="2026-06-10"/></div></div>
|
||
<div style="margin-top:12px;display:flex;gap:10px"><button class="btn primary" data-action="drawer" data-kind="generic" data-title="计划已生成">生成计划</button><button class="btn" data-route="exec-match">进入执行匹配 →</button></div></div></div>`;
|
||
break;
|
||
|
||
case "exec-match":
|
||
html += `<div class="panel"><div class="panel-head"><h2>资源匹配状态</h2><span class="pill v1">V1必做</span></div><div class="panel-body">
|
||
<div class="grid cols-3">
|
||
<div class="stat-card info"><div class="stat-num">1,280</div><div class="stat-label">候选人群 ${S()}</div><button class="btn small primary" style="margin-top:8px" data-action="drawer" data-kind="generic" data-title="匹配人群">匹配人群</button></div>
|
||
<div class="stat-card warn"><div class="stat-num">30</div><div class="stat-label">预占额度 / 月 ${S()}</div><button class="btn small" style="margin-top:8px" data-action="drawer" data-kind="generic" data-title="预占额度">预占额度</button></div>
|
||
<div class="stat-card info"><div class="stat-num">5</div><div class="stat-label">可用客服 ${S()}</div><button class="btn small" style="margin-top:8px" data-action="drawer" data-kind="generic" data-title="分配客服">分配客服</button></div></div>
|
||
<div style="margin-top:16px;display:flex;gap:10px">
|
||
<button class="btn primary" data-route="im-push">生成IM推送任务 →</button>
|
||
<button class="btn" data-action="drawer" data-kind="generic" data-title="释放预占">释放预占</button>
|
||
<button class="btn" data-route="koc-task" style="color:var(--purple)">匹配KOC/KOL <span class="sim-watermark">V1预留</span></button></div></div></div>`;
|
||
break;
|
||
|
||
case "plan-adjust":
|
||
html += `<div class="panel"><div class="panel-head"><h2>计划调整操作</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>计划编号</th><th>类型</th><th>进度</th><th>异常</th><th>操作</th></tr>
|
||
<tr><td>RP-0528-003</td><td>${t("测评","blue")}</td><td><div class="progress-bar"><div class="progress-fill red" style="width:32%"></div></div><small>9.6/30</small></td><td>${t("缺口大","red")}</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="调整名额">调整名额</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="补量">补量</button><button class="btn small danger" data-action="drawer" data-kind="generic" data-title="暂停计划">暂停</button></td></tr>
|
||
<tr><td>RP-0528-005</td><td>${t("回评","amber")}</td><td><div class="progress-bar"><div class="progress-fill green" style="width:80%"></div></div><small>12/15</small></td><td>产品禁用</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="恢复计划">恢复</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="转免评">转免评</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
// === 评价计划与订单 ===
|
||
case "review-plan":
|
||
html += statCards([{n:"12",l:"进行中",s:"info"},{n:"3",l:"暂停",s:"warn"},{n:"28",l:"已完成(月)",s:"good"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>测评计划列表</h2><button class="btn primary small" data-route="plan-builder">+ 新建</button></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>计划编号</th><th>产品/ASIN</th><th>目标</th><th>进度</th><th>评分</th><th>操作</th></tr>
|
||
<tr><td>RP-0528-003</td><td>B0XXXXXX01 / US</td><td>30</td><td><div class="progress-bar"><div class="progress-fill red" style="width:32%"></div></div><small>9.6/30</small></td><td>4.1</td><td><button class="btn small" data-route="review-order">订单</button><button class="btn small" data-route="im-push">推送</button><button class="btn small" data-route="plan-adjust">调整</button></td></tr>
|
||
<tr><td>RP-0528-006</td><td>B0XXXXXX05 / UK</td><td>20</td><td><div class="progress-bar"><div class="progress-fill green" style="width:60%"></div></div><small>12/20</small></td><td>4.3</td><td><button class="btn small" data-route="review-order">订单</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "reply-plan":
|
||
html += statCards([{n:"5",l:"进行中",s:"info"},{n:"2",l:"紧急(掉评)","s":"danger"},{n:"18",l:"已完成(月)",s:"good"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>回评计划</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>计划编号</th><th>ASIN</th><th>触发原因</th><th>今日目标</th><th>已完成</th><th>操作</th></tr>
|
||
<tr><td>RP-0528-007</td><td>B0XXXXXX03</td><td>${t("掉评","red")}</td><td>10</td><td>4</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="调整今日目标">调整目标</button><button class="btn small" data-route="reply-order">查看订单</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "free-plan":
|
||
html += `<div class="panel"><div class="panel-head"><h2>免评计划</h2><span class="pill v1">V1必做</span></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>计划编号</th><th>目标人群</th><th>目标</th><th>进度</th><th>渠道</th><th>操作</th></tr>
|
||
<tr><td>FP-0528-001</td><td>长期测评人(额度>=12)</td><td>20</td><td><div class="progress-bar"><div class="progress-fill green" style="width:25%"></div></div><small>5/20</small></td><td>IM+APP</td><td><button class="btn small" data-route="exec-match">匹配资源</button><button class="btn small" data-route="koc-task">关联KOC</button></td></tr>
|
||
<tr><td>FP-0528-002</td><td>KOC/KOL达人</td><td>15</td><td><div class="progress-bar"><div class="progress-fill blue" style="width:13%"></div></div><small>2/15</small></td><td>KOC协作</td><td><button class="btn small" data-route="koc-task">管理KOC任务</button></td></tr></table></div>
|
||
<div style="font-size:12px;color:var(--muted);margin-top:8px">⚠ 免评执行不以"用户提交评价"为终点。结果写入 exemption_plan_tasks、creator_content_records。</div></div>`;
|
||
break;
|
||
|
||
case "review-order":
|
||
html += statCards([{n:"45",l:"待登记",s:"warn"},{n:"128",l:"已登记",s:"info"},{n:"67",l:"待回评",s:"warn"},{n:"89",l:"待返款",s:"warn"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>测评订单</h2><div style="display:flex;gap:8px"><button class="btn primary small" data-action="drawer" data-kind="generic" data-title="新增订单">+ 新增订单</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="批量导出">📤 批量导出</button></div></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>订单编号</th><th>用户</th><th>Amazon订单</th><th>评价状态</th><th>返款状态</th><th>操作</th></tr>
|
||
<tr><td>RO-0528-001</td><td>JH000123</td><td>112-XXXXXXX-XXXXX</td><td>${t("已提交","blue")}</td><td>${t("待请款","amber")}</td><td><button class="btn small primary" data-route="refund-audit">请款</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="上传回评">上传回评</button></td></tr>
|
||
<tr><td>RO-0528-002</td><td>JH000456</td><td>-</td><td>${t("待提交","gray")}</td><td>-</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="上传订单">上传订单</button><button class="btn small danger" data-action="drawer" data-kind="generic" data-title="撤销">撤销</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "reply-order":
|
||
html += `<div class="panel"><div class="panel-head"><h2>回评订单</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>订单编号</th><th>用户</th><th>评论状态</th><th>展示核验</th><th>操作</th></tr>
|
||
<tr><td>RRO-0528-001</td><td>JH000321</td><td>${t("已提交","blue")}</td><td>${t("待核验","amber")}</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="回评确认">确认展示</button></td></tr>
|
||
<tr><td>RRO-0528-002</td><td>JH000654</td><td>${t("已展示","green")}</td><td>${t("已确认","green")}</td><td><button class="btn small" data-route="refund-audit">请款</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
// === 客服执行 ===
|
||
case "ticket-pool":
|
||
html += statCards([{n:"89",l:"待处理",s:"warn"},{n:"3",l:"超时未分配",s:"danger"},{n:"12",l:"在线客服",s:"good"},{n:"4.2min",l:"平均首次回复",s:"good"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>工单池</h2><div style="display:flex;gap:8px"><button class="btn primary small" data-action="drawer" data-kind="generic" data-title="自动分配">🔄 自动分配</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="手动分配">👤 手动分配</button></div></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>工单号</th><th>用户</th><th>渠道</th><th>问题类型</th><th>状态</th><th>等待</th><th>操作</th></tr>
|
||
<tr><td>TK-0528-089</td><td>JH000111</td><td>${t("IM","blue")}</td><td>订单登记</td><td>${t("待分配","red")}</td><td>12min</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="分配工单">分配</button></td></tr>
|
||
<tr><td>TK-0528-088</td><td>JH000222</td><td>${t("EDM","purple")}</td><td>返款账号补全</td><td>${t("处理中","blue")}</td><td>5min</td><td><button class="btn small" data-route="my-tickets">查看</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="转移工单">转移</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "my-tickets":
|
||
html += `<div class="grid cols-2">
|
||
<div class="panel"><div class="panel-head"><h2>TK-0528-088 · JH000222</h2><span class="pill amber">待处理</span></div><div class="panel-body">
|
||
<div style="background:var(--panel2);padding:12px;border-radius:6px;margin-bottom:12px">
|
||
<div class="chat-bubble user">你好,我的返款怎么还没到?</div>
|
||
<div class="chat-bubble agent">您好!请提供您的返款账号信息,我帮您查询。</div>
|
||
<div class="chat-bubble user">我的PayPal是 test@email.com</div></div>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
||
<button class="btn primary small" data-action="drawer" data-kind="generic" data-title="回复已发送">📨 回复</button>
|
||
<button class="btn small" data-route="review-order">📋 登记订单</button>
|
||
<button class="btn small" data-route="refund-audit">💰 发起请款</button>
|
||
<button class="btn small" data-action="drawer" data-kind="generic" data-title="标记解决">✓ 标记解决</button>
|
||
<button class="btn small" data-action="drawer" data-kind="generic" data-title="升级">⬆ 升级</button></div></div></div>
|
||
<div class="panel"><div class="panel-head"><h2>用户上下文卡 · JH000222</h2></div><div class="panel-body"><table>
|
||
<tr><th style="width:100px">JOYHUB ID</th><td>JH000222</td></tr><tr><th>额度</th><td>测评 2/4 · 免评 0/4 · 累计 5/12</td></tr><tr><th>关联订单</th><td>RO-0528-001 (待返款)</td></tr><tr><th>风险</th><td>${t("正常","green")}</td></tr><tr><th>标签</th><td>${t("参与过","blue")} ${t("待返款","amber")}</td></tr></table></div></div></div>`;
|
||
break;
|
||
|
||
case "chat-view":
|
||
html += `<div class="grid cols-2"><div class="panel"><div class="panel-head"><h2>用户消息列表</h2></div><div class="panel-body" style="max-height:400px;overflow-y:auto">
|
||
<div style="padding:8px;border-bottom:1px solid var(--line);background:var(--blue-s)"><strong>JH000222</strong> <small style="color:var(--muted)">10:30</small><br><small>我的返款怎么还没到?</small></div>
|
||
<div style="padding:8px;border-bottom:1px solid var(--line)"><strong>JH000444</strong> <small style="color:var(--muted)">09:15</small><br><small>我已经下单了</small></div></div></div>
|
||
<div class="panel"><div class="panel-head"><h2>当前对话 · JH000222</h2></div><div class="panel-body">
|
||
<div style="background:var(--panel2);padding:12px;border-radius:6px;min-height:200px">
|
||
<div class="chat-bubble user">你好,我的返款怎么还没到?</div>
|
||
<div class="chat-bubble agent">您好!请提供返款账号信息。</div></div>
|
||
<div style="display:flex;gap:8px;margin-top:8px"><input placeholder="输入消息..." style="flex:1;padding:7px;border:1px solid var(--line);border-radius:5px"/><button class="btn primary small">发送</button></div></div></div></div>`;
|
||
break;
|
||
|
||
case "promise-follow":
|
||
html += `<div class="panel"><div class="panel-head"><h2>答应配合跟踪</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>用户</th><th>答应时间</th><th>承诺内容</th><th>截止</th><th>状态</th><th>操作</th></tr>
|
||
<tr><td>JH000555</td><td>05-27</td><td>提交评价截图</td><td>05-29</td><td>${t("已确认","blue")}</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="提醒">提醒</button><button class="btn small danger" data-action="drawer" data-kind="generic" data-title="标记过期">过期</button></td></tr>
|
||
<tr><td>JH000777</td><td>05-28</td><td>提供返款账号</td><td>05-30</td><td>${t("待确认","amber")}</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="确认答应">确认</button><button class="btn small danger" data-action="drawer" data-kind="generic" data-title="拒绝">拒绝</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "reminder-pool":
|
||
html += `<div class="panel"><div class="panel-head"><h2>待催评列表</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>订单编号</th><th>用户</th><th>产品</th><th>登记日期</th><th>已催次数</th><th>操作</th></tr>
|
||
<tr><td>RO-0520-045</td><td>JH000888</td><td>B0XXXXXX01</td><td>05-20</td><td>2</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="发送催评">催评</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="转人工">转人工</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
// === 客服管理 ===
|
||
case "cs-dashboard":
|
||
html += statCards([{n:"12",l:"在线客服",s:"good"},{n:"89",l:"今日工单",s:"info"},{n:"23",l:"待处理",s:"warn"},{n:"67%",l:"今日转化率",s:"warn"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>客服实时状态</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>客服</th><th>班次</th><th>当前工单</th><th>今日解决</th><th>首次回复</th><th>状态</th></tr>
|
||
<tr><td>张三</td><td>早班(US)</td><td>3</td><td>5</td><td>3.5min</td><td>${t("在线","green")}</td></tr>
|
||
<tr><td>李四</td><td>午班(US)</td><td>5</td><td>8</td><td>4.8min</td><td>${t("在线","green")}</td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "attendance":
|
||
html += `<div class="panel"><div class="panel-head"><h2>出勤管理 · 2026-05-28</h2><button class="btn small" data-action="drawer" data-kind="generic" data-title="导入出勤">📥 导入出勤 <span class="sim-watermark">V1预留</span></button></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>客服</th><th>应出勤</th><th>实际</th><th>状态</th><th>操作</th></tr>
|
||
<tr><td>张三</td><td>08:00-16:00</td><td>✅</td><td>${t("正常","green")}</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="调整出勤">调整</button></td></tr>
|
||
<tr><td>赵六</td><td>16:00-24:00</td><td>❌</td><td>${t("请假","gray")}</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="调整出勤">调整</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "scheduling":
|
||
html += `<div class="panel"><div class="panel-head"><h2>本周排班</h2><div style="display:flex;gap:8px"><button class="btn primary small" data-action="drawer" data-kind="generic" data-title="设置班次">+ 设置班次</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="批量排班">批量排班</button></div></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>客服</th><th>周一</th><th>周二</th><th>周三</th><th>周四</th><th>周五</th></tr>
|
||
<tr><td>张三</td><td>早班</td><td>早班</td><td>休息</td><td>早班</td><td>早班</td></tr>
|
||
<tr><td>李四</td><td>午班</td><td>午班</td><td>午班</td><td>午班</td><td>休息</td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "cs-performance":
|
||
html += `<div class="panel"><div class="panel-head"><h2>转化绩效 · 2026-05</h2><button class="btn small" data-action="drawer" data-kind="generic" data-title="导出绩效">📤 导出</button></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>客服</th><th>RSO登记</th><th>RDO上评</th><th>完成率</th><th>满意度</th><th>首次回复</th><th>月目标进度</th></tr>
|
||
<tr><td>张三</td><td>42</td><td>28</td><td>67%</td><td>91%</td><td>3.5min</td><td><div class="progress-bar"><div class="progress-fill green" style="width:84%"></div></div><small>84%</small></td></tr>
|
||
<tr><td>李四</td><td>38</td><td>22</td><td>58%</td><td>87%</td><td>4.8min</td><td><div class="progress-bar"><div class="progress-fill amber" style="width:78%"></div></div><small>78%</small></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "cs-target":
|
||
html += `<div class="panel"><div class="panel-head"><h2>月度目标设置</h2><button class="btn primary small" data-action="drawer" data-kind="generic" data-title="设置月目标">设置月目标</button></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>客服</th><th>月份</th><th>RSO目标</th><th>RSO实际</th><th>RDO目标</th><th>RDO实际</th><th>完成率</th></tr>
|
||
<tr><td>张三</td><td>2026-05</td><td>50</td><td>42</td><td>35</td><td>28</td><td>${t("84%","blue")}</td></tr></table></div></div>`;
|
||
break;
|
||
|
||
// === 渠道 ===
|
||
case "im-push":
|
||
html += statCards([{n:"12,450",l:"今日推送",s:"info"},{n:"398",l:"回复",s:"good"},{n:"3.2%",l:"回复率",s:"warn"},{n:"45",l:"退订",s:"warn"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>IM推送任务</h2><button class="btn primary small" data-action="drawer" data-kind="generic" data-title="新增推送">+ 新增推送</button></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>任务编号</th><th>人群数</th><th>卡片</th><th>已发送</th><th>回复率</th><th>状态</th><th>操作</th></tr>
|
||
<tr><td>IM-0528-001</td><td>1,280</td><td>卡片_B0XXXXXX01_v2</td><td>890</td><td>3.1%</td><td>${t("发送中","blue")}</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="上架/下架">下架</button><button class="btn small" data-route="channel-funnel">漏斗</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "im-card":
|
||
html += `<div class="panel"><div class="panel-head"><h2>IM卡片/H5管理</h2><button class="btn primary small" data-action="drawer" data-kind="generic" data-title="新增卡片">+ 新增卡片</button></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>卡片编号</th><th>关联产品</th><th>类型</th><th>状态</th><th>操作</th></tr>
|
||
<tr><td>CARD-001</td><td>B0XXXXXX01</td><td>${t("测评","blue")}</td><td>${t("已上架","green")}</td><td><button class="btn small danger" data-action="drawer" data-kind="generic" data-title="下架卡片">下架</button></td></tr>
|
||
<tr><td>CARD-002</td><td>B0XXXXXX03</td><td>${t("回评","amber")}</td><td>${t("已下架","gray")}</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="重新上架">重新上架</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "edm-check":
|
||
html += `<div class="alert-row p0" style="margin-bottom:12px"><span style="font-weight:700">P0</span> EDM域名信誉异常,已暂停发送 | 域名:mail.joyhub.example.com</div>`;
|
||
html += statCards([{n:"异常",l:"域名信誉",s:"danger"},{n:"正常",l:"邮箱状态",s:"good"},{n:"2,340",l:"今日发送",s:"info"},{n:"0.8%",l:"退订率",s:"good"}]);
|
||
html += `<div class="grid cols-2"><div class="panel"><div class="panel-head"><h2>基础设施检查</h2></div><div class="panel-body"><table>
|
||
<tr><th>检查项</th><th>状态</th></tr><tr><td>域名信誉</td><td>${t("异常","red")}</td></tr><tr><td>邮箱状态</td><td>${t("正常","green")}</td></tr><tr><td>IP信誉</td><td>${t("正常","green")}</td></tr><tr><td>UID人群</td><td>${t("正常","green")} 12,450人</td></tr></table></div></div>
|
||
<div class="panel"><div class="panel-head"><h2>今日EDM任务</h2></div><div class="panel-body"><table>
|
||
<tr><th>任务</th><th>发送</th><th>打开率</th><th>点击率</th><th>退订率</th></tr><tr><td>EDM-0528-A</td><td>2,340</td><td>18.2%</td><td>3.5%</td><td>0.8%</td></tr></table></div></div></div>`;
|
||
break;
|
||
|
||
case "channel-funnel":
|
||
html += `<div class="grid cols-2">
|
||
<div class="panel"><div class="panel-head"><h2>IM漏斗</h2></div><div class="panel-body">
|
||
${funnelRow("推送","12,450","100%","blue")}${funnelRow("曝光","8,960","72%","blue")}${funnelRow("点击","1,245","10%","blue")}${funnelRow("回复","398","3.2%","red")}${funnelRow("订单","128","1.0%","green")}${funnelRow("评价","45","0.36%","green")}</div></div>
|
||
<div class="panel"><div class="panel-head"><h2>EDM漏斗</h2></div><div class="panel-body">
|
||
${funnelRow("发送","2,340","100%","purple")}${funnelRow("打开","426","18.2%","purple")}${funnelRow("点击","82","3.5%","purple")}${funnelRow("退订","19","0.8%","amber")}</div></div></div>`;
|
||
break;
|
||
|
||
// === KOC/KOL ===
|
||
case "koc-leads":
|
||
html += `<div class="panel"><div class="panel-head"><h2>KOC/KOL线索池</h2><span class="pill purple v2">V1预留 · V2实现</span></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>线索ID</th><th>来源</th><th>名称</th><th>国家</th><th>分层</th><th>风险</th><th>操作</th></tr>
|
||
<tr><td>KOC-001</td><td>JOYHUB</td><td>@beauty_expert</td><td>US</td><td>${t("垂直专家","purple")}</td><td>${t("正常","green")}</td><td><button class="btn small" data-route="koc-profile">建档</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="打标分层">分层</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "koc-profile":
|
||
html += `<div class="panel"><div class="panel-head"><h2>达人档案 · @beauty_expert</h2><span class="pill purple v2">V1预留</span></div><div class="panel-body grid cols-2">
|
||
<div><table><tr><th>KOC ID</th><td>KOC-001</td></tr><tr><th>国家</th><td>US</td></tr><tr><th>品类</th><td>美妆</td></tr><tr><th>粉丝</th><td>45K</td></tr><tr><th>分层</th><td>${t("垂直专家","purple")}</td></tr></table></div>
|
||
<div><table><tr><th>合作状态</th><td>${t("可合作","green")}</td></tr><tr><th>内容能力</th><td>图文+短视频</td></tr><tr><th>带货能力</th><td>中等 · 2.1%</td></tr><tr><th>关联测评人</th><td>JH000420 (同一真实人)</td></tr></table></div></div>
|
||
<div style="font-size:12px;color:var(--amber);margin-top:8px">⚠ KOC/KOL身份与测评人身份可能重叠,需区分业务对象和任务完成口径。</div></div>`;
|
||
break;
|
||
|
||
case "koc-task":
|
||
html += `<div class="panel"><div class="panel-head"><h2>合作任务</h2><span class="pill purple v2">V1预留 · 免评补单V1必做</span></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>任务ID</th><th>达人</th><th>类型</th><th>产品</th><th>状态</th><th>操作</th></tr>
|
||
<tr><td>KT-0528-001</td><td>@beauty_expert</td><td>${t("免评补单","purple")}</td><td>B0XXXXXX04</td><td>${t("待执行","blue")}</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="上传内容链接">上传内容 <span class="sim-watermark">V2</span></button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
// === 测评人/真实人 ===
|
||
case "reviewer-list":
|
||
html += statCards([{n:"8,450",l:"总测评人",s:"info"},{n:"4,230",l:"可用(额度未满)",s:"good"},{n:"1,280",l:"US站可用",s:"info"},{n:"156",l:"黑名单",s:"danger"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>测评人列表</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>编号</th><th>JOYHUB ID</th><th>国家</th><th>月度测评</th><th>累计评价</th><th>风险</th><th>操作</th></tr>
|
||
<tr><td>REV-0001</td><td>JH000123</td><td>US</td><td>2/4</td><td>5/12</td><td>${t("正常","green")}</td><td><button class="btn small" data-route="quota-ledger">额度</button></td></tr>
|
||
<tr><td>REV-0002</td><td>JH000456</td><td>UK</td><td>4/4</td><td>11/12</td><td>${t("正常","green")}</td><td><button class="btn small" data-route="quota-ledger">额度</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "quota-ledger":
|
||
html += `<div class="grid cols-3">
|
||
<div class="stat-card good"><div class="stat-num">2/4</div><div class="stat-label">月度测评额度 ${S()}</div></div>
|
||
<div class="stat-card info"><div class="stat-num">0/4</div><div class="stat-label">月度免评额度 ${S()}</div></div>
|
||
<div class="stat-card warn"><div class="stat-num">5/12</div><div class="stat-label">累计评价提交 ${S()}</div></div></div>
|
||
<div style="font-size:12px;color:var(--muted);margin:8px 0">规则:同一真实人月度测评≤4、免评≤4、累计提交评价≤12。计数时点=用户真实提交评价时。</div>
|
||
<div class="panel"><div class="panel-head"><h2>额度明细 · REV-0001</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>日期</th><th>类型</th><th>订单</th><th>操作</th><th>额度变化</th><th>剩余</th></tr>
|
||
<tr><td>05-28</td><td>测评</td><td>RO-0528-001</td><td>预占</td><td>2→3/4</td><td>1/4</td></tr>
|
||
<tr><td>05-20</td><td>测评</td><td>RO-0520-015</td><td>提交评价</td><td>累计 4→5/12</td><td>7/12</td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "identity-merge":
|
||
html += `<div class="panel"><div class="panel-head"><h2>真实人归并</h2><span class="pill v1">V1必做(自动归并)</span></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>真实人ID</th><th>JOYHUB ID</th><th>邮箱</th><th>归并置信度</th><th>额度汇总</th><th>操作</th></tr>
|
||
<tr><td>PERSON-0001</td><td>JH000123, JH000420</td><td>same@email.com</td><td>${t("高 95%","green")}</td><td>测评3/4 累计6/12</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="手动合并">合并 <span class="sim-watermark">预留</span></button><button class="btn small danger" data-action="drawer" data-kind="generic" data-title="手动拆分">拆分 <span class="sim-watermark">预留</span></button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
// === 风险 ===
|
||
case "risk-events":
|
||
html += statCards([{n:"7",l:"待复核",s:"warn"},{n:"3",l:"P0事件",s:"danger"},{n:"15",l:"今日拦截",s:"good"},{n:"2",l:"新增黑名单",s:"info"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>风险事件</h2><button class="btn primary small" data-action="drawer" data-kind="generic" data-title="创建风险事件">+ 创建</button></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>事件ID</th><th>等级</th><th>类型</th><th>关联对象</th><th>状态</th><th>操作</th></tr>
|
||
<tr><td>RS-0528-001</td><td>${t("P0","red")}</td><td>双重退款</td><td>RO-0528-001</td><td>${t("待复核","red")}</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="复核通过">确认</button><button class="btn small" data-action="drawer" data-kind="generic" data-title="排除风险">排除</button></td></tr>
|
||
<tr><td>RS-0528-002</td><td>${t("P1","amber")}</td><td>额度超限</td><td>JH000789</td><td>${t("复核中","amber")}</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="复核通过">确认</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "blacklist":
|
||
html += `<div class="panel"><div class="panel-head"><h2>黑名单</h2><button class="btn danger small" data-action="drawer" data-kind="generic" data-title="新增黑名单">+ 新增黑名单</button></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>条目ID</th><th>对象</th><th>类型</th><th>风险等级</th><th>来源</th><th>状态</th></tr>
|
||
<tr><td>BL-001</td><td>JH000999</td><td>测评人</td><td>${t("强风险","red")}</td><td>双重退款</td><td>${t("生效中","red")}</td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "refund-compare":
|
||
html += `<div class="panel"><div class="panel-head"><h2>Amazon退款 vs OA返款双重比对</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>比对ID</th><th>Amazon订单</th><th>Amazon退款</th><th>OA返款</th><th>重合</th><th>操作</th></tr>
|
||
<tr><td>CMP-0528-001</td><td>112-XXXXXXX</td><td>$29.99 (05-27)</td><td>¥210 (05-28)</td><td>${t("确认","red")}</td><td><button class="btn small danger" data-route="risk-events">生成风险事件</button></td></tr>
|
||
<tr><td>CMP-0528-002</td><td>113-XXXXXXX</td><td>$19.99 (05-26)</td><td>¥140 (05-28)</td><td>${t("疑似","amber")}</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="标记">标记</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
// === 财务 ===
|
||
case "refund-audit":
|
||
html += `<div class="panel"><div class="panel-head"><h2>待审核请款</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>请款编号</th><th>关联订单</th><th>用户</th><th>金额</th><th>方式</th><th>风险</th><th>操作</th></tr>
|
||
<tr><td>RF-0528-042</td><td>RO-0528-001</td><td>JH000123</td><td>¥210</td><td>PayPal</td><td>${t("正常","green")}</td><td><button class="btn small primary" data-action="drawer" data-kind="generic" data-title="审核通过">✓ 通过</button><button class="btn small danger" data-action="drawer" data-kind="generic" data-title="审核拒绝">✕ 拒绝</button></td></tr>
|
||
<tr><td>RF-0528-043</td><td>RO-0528-003</td><td>JH000789</td><td>¥350</td><td>礼品卡</td><td>${t("超额","amber")}</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="超额审核">超额审核</button></td></tr></table></div></div>
|
||
<div class="panel"><div class="panel-head"><h2>待返款队列</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>返款编号</th><th>用户</th><th>金额</th><th>方式</th><th>状态</th><th>操作</th></tr>
|
||
<tr><td>RF-0527-038</td><td>JH000321</td><td>¥180</td><td>PayPal</td><td>${t("待返款","amber")}</td><td><button class="btn small" data-action="drawer" data-kind="generic" data-title="确认返款">确认返款</button></td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "refund-records":
|
||
html += statCards([{n:"42",l:"待审核",s:"warn"},{n:"156",l:"待返款",s:"info"},{n:"¥156,200",l:"本月已返款",s:"good"},{n:"3",l:"异常锁定",s:"danger"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>返款记录</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>返款编号</th><th>订单</th><th>用户</th><th>金额</th><th>方式</th><th>状态</th></tr>
|
||
<tr><td>RF-0527-035</td><td>RO-0527-020</td><td>JH000111</td><td>¥195</td><td>PayPal</td><td>${t("成功","green")}</td></tr>
|
||
<tr><td>RF-0527-037</td><td>RO-0527-022</td><td>JH000333</td><td>¥280</td><td>PayPal</td><td>${t("锁定","red")}</td></tr></table></div></div>`;
|
||
break;
|
||
|
||
// === 复盘看板 ===
|
||
case "report-plan":
|
||
html += statCards([{n:"48",l:"总计划(月)",s:"info"},{n:"15",l:"进行中",s:"info"},{n:"28",l:"已完成",s:"good"},{n:"83%",l:"完成率",s:"good"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>计划完成趋势</h2></div><div class="panel-body" style="text-align:center;padding:40px;color:var(--muted)">[ 计划完成趋势图 - Stage 2 实现 ]<br><small>横轴:日期 | 纵轴:完成数 | 折线:测评/回评/免评</small></div></div>`;
|
||
break;
|
||
|
||
case "report-asin":
|
||
html += `<div class="panel"><div class="panel-head"><h2>ASIN健康度</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>ASIN</th><th>当前评分</th><th>Review数</th><th>近7天掉评</th><th>近7天差评</th><th>库存</th><th>风险</th></tr>
|
||
<tr><td>B0XXXXXX01</td><td>4.1</td><td>86</td><td>${t("2","amber")}</td><td>1</td><td>200</td><td>${t("低","green")}</td></tr>
|
||
<tr><td>B0XXXXXX03</td><td>3.8</td><td>45</td><td>${t("5","red")}</td><td>${t("3","red")}</td><td>充足</td><td>${t("高","red")}</td></tr></table></div></div>`;
|
||
break;
|
||
|
||
case "report-channel":
|
||
html += `<div class="grid cols-3">
|
||
<div class="stat-card info"><div class="stat-num">12,450</div><div class="stat-label">IM日推送量 ${S()}</div></div>
|
||
<div class="stat-card info"><div class="stat-num">2,340</div><div class="stat-label">EDM日发送量 ${S()}</div></div>
|
||
<div class="stat-card warn"><div class="stat-num">3.2%</div><div class="stat-label">IM回复率 ${S()}</div></div>
|
||
<div class="stat-card good"><div class="stat-num">0.8%</div><div class="stat-label">EDM退订率 ${S()}</div></div>
|
||
<div class="stat-card info"><div class="stat-num">1.0%</div><div class="stat-label">IM→订单转化 ${S()}</div></div>
|
||
<div class="stat-card good"><div class="stat-num">0.36%</div><div class="stat-label">IM→评价转化 ${S()}</div></div></div>`;
|
||
break;
|
||
|
||
case "report-cs":
|
||
html += statCards([{n:"1,560",l:"本月工单",s:"info"},{n:"92%",l:"解决率",s:"good"},{n:"4.2min",l:"平均首次回复",s:"good"},{n:"88%",l:"满意度",s:"good"}]);
|
||
html += `<div class="panel"><div class="panel-head"><h2>客服月度表现</h2></div><div class="panel-body table-wrap"><table>
|
||
<tr><th>客服</th><th>工单</th><th>解决率</th><th>满意度</th><th>RSO</th><th>RDO</th></tr>
|
||
<tr><td>张三</td><td>156</td><td>94%</td><td>91%</td><td>42</td><td>28</td></tr>
|
||
<tr><td>李四</td><td>142</td><td>89%</td><td>87%</td><td>38</td><td>22</td></tr></table></div></div>`;
|
||
break;
|
||
|
||
default: html += '<div class="panel"><div class="panel-body"><p>页面建设中...</p></div></div>';
|
||
}
|
||
$("content").innerHTML = html;
|
||
}
|
||
|
||
function funnelRow(label,num,pct,tone){
|
||
return `<div style="margin:10px 0"><div style="display:flex;justify-content:space-between;font-size:12px"><span>${esc(label)}</span><span>${esc(num)} (${esc(pct)})</span></div><div class="progress-bar"><div class="progress-fill ${tone}" style="width:${pct.replace('%','')}%"></div></div></div>`;
|
||
}
|
||
|
||
// ===== DRAWER =====
|
||
function openDrawer(kind,payload={}){
|
||
const drawer=$("drawer"),mask=$("drawerMask"),title=$("drawerTitle"),note=$("drawerNote"),body=$("drawerBody"),foot=$("drawerFoot");
|
||
let route=payload.route||payload.routeTarget||state.route;
|
||
if(kind==="source"){
|
||
title.textContent="来源与模拟说明";
|
||
note.textContent="本HTML为本地点击参考原型,所有业务数据均为模拟。";
|
||
body.innerHTML=`<ul class="list">${sourceDocs.map(d=>`<li><strong>${esc(d)}</strong><span>已作为页面结构、按钮、分支、异常、复盘或验证依据。</span></li>`).join("")}</ul>`;
|
||
foot.innerHTML=`<button class="btn primary" data-route="validation">查看验证记录</button>`;
|
||
}else if(kind==="action"){
|
||
const a=actionRows.find(x=>x.id===payload.id);
|
||
route=a.route;
|
||
title.textContent=`${a.id} · ${a.name}`;
|
||
note.textContent=`${a.page} · ${a.stage} · 审计:${a.audit}`;
|
||
body.innerHTML=`${infoBox("业务含义",a.meaning,"blue")}${infoBox("读取对象",a.read,"gray")}${infoBox("写入对象",a.write,"gray")}${infoBox("状态变化",a.change,"green")}${infoBox("权限",a.role,"amber")}<div class="source-bar"><strong>演示结果</strong><span>已模拟生成操作日志、通知、状态变化和复盘引用。</span><span class="pill amber">未写入真实系统</span></div>`;
|
||
foot.innerHTML=`<button class="btn" data-route="action-matrix">返回矩阵</button><button class="btn primary" data-route="${a.route}">跳转到相关页面</button>`;
|
||
}else if(kind==="flow"){
|
||
const data=WF[payload.routeKey]||WF["demand-pool"];
|
||
title.textContent="流程写入对象";
|
||
note.textContent="模拟展示流程节点与后续Stage 2对象设计的关系。";
|
||
body.innerHTML=`${flow(data.steps)}<ul class="list">${data.steps.map((s,i)=>`<li><strong>${esc(s)}</strong><span>模拟写入:状态日志、负责人、时间戳、复检结果、复盘引用 #${i+1}</span></li>`).join("")}</ul>`;
|
||
foot.innerHTML=`<button class="btn primary" data-route="${payload.routeKey}">返回当前页</button>`;
|
||
}else{
|
||
title.textContent=payload.title||"处理演示";
|
||
note.textContent="模拟从发现到关闭的异常状态流。";
|
||
body.innerHTML=`${flow(["待发现","已发现","待处理","处理中","待复核","已关闭"])}${infoBox("处理要求","当天能影响目标、资金、合规或账号安全的异常按P0/P1进入主管视图。","red")}${infoBox("关闭条件","必须有处理结论、责任人、操作日志、是否回流计划/客服/风险/复盘。","green")}`;
|
||
foot.innerHTML=`<button class="btn" data-route="exceptions">查看异常总表</button><button class="btn primary" data-route="${route}">跳转相关页面</button>`;
|
||
}
|
||
drawer.classList.add("open");mask.classList.add("open");
|
||
}
|
||
|
||
function closeDrawer(){$("drawer").classList.remove("open");$("drawerMask").classList.remove("open");}
|
||
|
||
function toast(text){
|
||
const el=$("toast");el.textContent=text;el.classList.add("open");
|
||
clearTimeout(window.__toastTimer);window.__toastTimer=setTimeout(()=>el.classList.remove("open"),1800);
|
||
}
|
||
|
||
// ===== EVENTS =====
|
||
function bindEvents(){
|
||
document.body.addEventListener("click",event=>{
|
||
const moduleBtn=event.target.closest("[data-module]");
|
||
if(moduleBtn){const m=modules.find(x=>x.id===moduleBtn.dataset.module);if(m)setRoute(m.pages[0]);return;}
|
||
const routeBtn=event.target.closest("[data-route]");
|
||
if(routeBtn){closeDrawer();setRoute(routeBtn.dataset.route);return;}
|
||
const actionBtn=event.target.closest("[data-action-id]");
|
||
if(actionBtn){openDrawer("action",{id:actionBtn.dataset.actionId});return;}
|
||
const action=event.target.closest("[data-action]");
|
||
if(action){if(action.dataset.action==="closeDrawer")closeDrawer();if(action.dataset.action==="drawer")openDrawer(action.dataset.kind,action.dataset);return;}
|
||
const roleCard=event.target.closest("[data-role-card]");
|
||
if(roleCard){state.role=roleCard.dataset.roleCard;$("roleSelect").value=state.role;renderContent();toast(`已切换到:${roles[state.role].name}`);}
|
||
});
|
||
$("drawerMask").addEventListener("click",closeDrawer);
|
||
$("roleSelect").addEventListener("change",event=>{
|
||
state.role=event.target.value;
|
||
if(state.route==="dashboard"||state.route==="role-workflow")renderContent();
|
||
$("topPageSubtitle").textContent=`${PD[state.route].owner} · 当前角色 ${roles[state.role].name}`;
|
||
toast(`角色视图:${roles[state.role].name}`);
|
||
});
|
||
$("period").addEventListener("click",event=>{
|
||
const btn=event.target.closest("button[data-period]");if(!btn)return;
|
||
state.period=btn.dataset.period;
|
||
$("period").querySelectorAll("button").forEach(b=>b.classList.toggle("active",b===btn));
|
||
setRoute(state.route,false);toast(`已切换为${btn.textContent}视图`);
|
||
});
|
||
$("globalSearch").addEventListener("keydown",event=>{
|
||
if(event.key!=="Enter")return;
|
||
openDrawer("generic",{title:`搜索:${event.target.value||"空关键词"}`,routeTarget:"demand-pool"});
|
||
});
|
||
window.addEventListener("hashchange",()=>{
|
||
const route=location.hash.replace("#","");
|
||
if(route&&route!==state.route&&PD[route])setRoute(route,false);
|
||
});
|
||
}
|
||
|
||
init();
|
||
</script>
|
||
<div style="position:fixed;bottom:12px;right:16px;font-size:11px;color:var(--muted);z-index:300;background:var(--panel);padding:4px 10px;border-radius:4px;border:1px solid var(--line);">
|
||
⚠ 本原型所有数据均为模拟数据,仅用于 Stage 1 需求验证 · v2
|
||
</div>
|
||
</body>
|
||
</html> |