Files
Fulfilled-Knowledge/wishfulfilled-wiki/05_需求文档/20260528_USER_ERP_完整参考原型_v2.html
2026-05-29 14:33:56 +08:00

1107 lines
110 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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=>({"&":"&amp;","<":"&lt;",">":"&gt;","\"":"&quot;","'":"&#039;"}[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>