Files
2026-10-28 11:35:48 +08:00

95 lines
38 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">
<title>29 供应链 · 店铺管理设计演示</title>
<style>
:root{--brand:#98638f;--brand-dark:#784b70;--tint:#f4edf3;--ink:#292731;--muted:#85828e;--line:#e5e7eb;--canvas:#f5f6f7;--green:#31856a;--red:#c35c64;--amber:#a77725}
*{box-sizing:border-box}body{margin:0;font:14px/1.5 "Segoe UI","Microsoft YaHei",sans-serif;color:var(--ink);background:var(--canvas)}button,input,select{font:inherit}button{cursor:pointer;white-space:nowrap}button,input,select{border:1px solid #d1d5db;border-radius:6px;background:white;color:inherit}button{padding:8px 15px;transition:background .15s}button:hover:not(:disabled){background:#f0f2f4;border-color:#b9c0c8}button:disabled{background:#f5f6f7;color:#a0a6ad;border-color:#e2e5e9;cursor:default}button:focus-visible,input:focus-visible,select:focus-visible,a:focus-visible{outline:2px solid #64748b;outline-offset:2px}input,select{height:36px;padding:0 11px;min-width:0}input[type=checkbox]{width:15px;height:15px;accent-color:var(--brand);vertical-align:middle;cursor:pointer}button.primary{background:var(--brand);color:#fff;border-color:transparent}button.primary:hover:not(:disabled){background:var(--brand-dark);border-color:transparent}button.text{border:0;background:none;color:#626970;padding:5px 8px}button.text:hover:not(:disabled){background:#e9edf0;color:#30353b}.muted{color:var(--muted)}.mono{font-family:Consolas,monospace;font-size:12px;color:#777482}.app{display:grid;grid-template-columns:218px minmax(0,1fr);height:100vh;min-height:680px}.sidebar{background:#fff;border-right:1px solid var(--line);display:flex;flex-direction:column;padding:28px 16px 16px}.brand{padding:0 12px 30px;display:flex;align-items:center;gap:10px}.brand b{font-size:29px;color:var(--brand);letter-spacing:-2px}.brand strong{font-size:17px;letter-spacing:1px}.brand small{display:block;color:var(--muted);font-size:10px;letter-spacing:2px}.section-label{padding:15px 14px 8px;color:#9a97a1;font-size:11px;letter-spacing:1.4px}.nav{display:flex;align-items:center;gap:13px;width:100%;border:0;text-align:left;padding:12px 14px;margin:3px 0;background:none;color:#65616e;border-radius:7px}.nav .nav-index{font:11px Consolas;color:#a6a0aa}.nav.active{background:var(--tint);color:var(--brand-dark);font-weight:600}.nav.active .nav-index{color:var(--brand)}.nav.active:after{content:"";width:5px;height:5px;border-radius:50%;background:var(--brand);margin-left:auto}.sidebar-bottom{margin-top:auto;padding:18px 12px 0;border-top:1px solid var(--line)}.demo-badge{font-size:11px;background:var(--tint);color:var(--brand-dark);padding:3px 7px;border-radius:4px}.sidebar-bottom p{font-size:11px;color:var(--muted);line-height:1.8}.shell{min-width:0;display:flex;flex-direction:column;overflow:hidden}.topbar{height:66px;flex-shrink:0;background:white;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;padding:0 30px}.breadcrumb{font-size:12px;color:var(--muted)}.breadcrumb span{color:var(--ink);padding-left:10px}.profile{display:flex;align-items:center;gap:20px;font-size:12px}.profile b{color:var(--brand-dark);font-weight:600}.avatar{background:var(--tint);color:var(--brand-dark);padding:8px 10px;border-radius:8px;font-weight:600}.content{padding:26px 30px 18px;flex:1;min-height:0;display:flex;flex-direction:column;gap:20px}.page-heading{display:flex;justify-content:space-between;align-items:center;gap:20px}.eyebrow{font-size:10px;letter-spacing:2px;color:var(--brand);font-weight:700;margin-bottom:4px}h1{font-size:25px;letter-spacing:.5px;margin:0;font-weight:650}.page-heading p{font-size:12px;color:var(--muted);margin:6px 0 0}.heading-actions{display:flex;gap:10px}.stats{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);background:white;border-radius:9px;padding:19px 0}.stat{padding:0 25px;border-right:1px solid var(--line);position:relative}.stat:last-child{border:0}.stat-label{color:#77727f;font-size:12px}.stat-number{font-size:27px;font-weight:650;margin-top:5px;font-variant-numeric:tabular-nums;letter-spacing:-.5px}.stat-note{position:absolute;right:23px;bottom:6px;font-size:11px;color:var(--muted)}.stat:nth-child(2) .stat-number{color:var(--green)}.stat:nth-child(3) .stat-number{color:var(--amber)}.stat:nth-child(4) .stat-number{color:var(--brand)}.workspace{background:#fff;border:1px solid var(--line);border-radius:9px;display:flex;flex:1;min-height:0;flex-direction:column;overflow:hidden}.tabs{display:flex;gap:26px;margin:0 22px;border-bottom:1px solid var(--line);min-height:53px;align-items:stretch}.tab{border:0;border-radius:0;padding:0 2px;background:transparent;font-size:13px;color:#898390;position:relative}.tab.active{color:var(--brand-dark);font-weight:600}.tab.active:after{content:"";height:3px;background:var(--brand);position:absolute;bottom:0;left:0;right:0;border-radius:3px 3px 0 0}.tab small{margin-left:7px;font-size:11px;color:inherit;background:#f5f3f6;padding:2px 6px;border-radius:4px}.filters{display:flex;align-items:end;gap:12px;padding:18px 22px}.field{display:flex;flex-direction:column;gap:7px}.field label{font-size:11px;color:#77717f}.search-field{flex:1;max-width:330px}.field select{width:150px}.toolbar{display:flex;justify-content:space-between;align-items:center;padding:0 22px 14px;gap:12px;font-size:12px}.toolbar-left,.toolbar-right{display:flex;gap:9px;align-items:center}.toolbar button{font-size:12px;padding:6px 10px}.selection{color:var(--brand-dark)}.table-scroll{overflow:auto;flex:1;min-height:130px;border-top:1px solid var(--line)}table{width:100%;border-collapse:separate;border-spacing:0;white-space:nowrap;font-size:12px}th{position:sticky;top:0;z-index:1;text-align:left;background:#f8f7fa;color:#817b89;font-size:11px;font-weight:500;height:41px;border-bottom:1px solid var(--line);padding:0 16px}td{height:65px;border-bottom:1px solid #f0eef3;padding:8px 16px}tbody tr:hover td{background:#faf7fa}tbody tr.selected td{background:#f7f0f6}th:first-child,td:first-child{width:46px;padding-left:22px;padding-right:7px}td:last-child,th:last-child{position:sticky;right:0;background:#fff;box-shadow:-8px 0 12px -13px #807488}th:last-child{background:#f8f7fa}.entity{display:flex;align-items:center;gap:11px}.entity-mark{height:34px;width:34px;border-radius:7px;background:#f1edf5;color:#8c769d;display:grid;place-items:center;font-size:13px;flex-shrink:0}.entity-mark.alt{background:#eef3f1;color:#719485}.entity-name{font-weight:600;font-size:12px;margin-bottom:2px}.entity small{font-size:10px;color:#97909f}.pill{display:inline-flex;align-items:center;gap:6px;padding:3px 8px;border-radius:4px;font-size:11px;background:#edf6f1;color:var(--green)}.pill:before{content:"";width:4px;height:4px;background:currentColor;border-radius:50%}.pill.warn{color:var(--amber);background:#fbf4e8}.pill.error{color:var(--red);background:#fbefef}.tag{font-size:11px;padding:3px 7px;background:#f4f3f6;border-radius:4px;color:#77717f}.date{font-variant-numeric:tabular-nums;color:#686370;font-size:11px}.subline{font-size:10px;color:var(--muted);margin-top:4px}.row-action{font-size:12px!important}.empty{text-align:center;height:160px;color:var(--muted)}.pagination{padding:14px 22px;display:flex;align-items:center;justify-content:space-between;font-size:11px;color:#89828f;border-top:1px solid var(--line)}.pagination-controls{display:flex;gap:7px;align-items:center}.pagination button{height:29px;min-width:30px;padding:3px 8px;font-size:12px}.pagination select{height:29px;font-size:11px}.current-page{background:var(--tint);color:var(--brand-dark);padding:5px 11px;border-radius:4px}.statusbar{display:flex;justify-content:space-between;color:#99929f;font-size:10px;padding-top:0}.statusbar span:first-child{color:#7b8e82}.compact td{height:43px;padding-top:4px;padding-bottom:4px}.compact .entity-mark{width:28px;height:28px}.compact .entity small{display:none}dialog{border:1px solid var(--line);padding:0;border-radius:12px;color:var(--ink);box-shadow:0 20px 90px #3426382b}dialog::backdrop{background:#25202c45}.drawer{margin:0 0 0 auto;height:100vh;max-height:100vh;width:480px;max-width:95vw;border-radius:12px 0 0 12px}.dialog-head{padding:24px 26px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center}.dialog-head h2{font-size:19px;margin:0}.dialog-head p{color:var(--muted);font-size:11px;margin:5px 0 0}.dialog-body{padding:24px 26px;display:flex;flex-direction:column;gap:19px}.dialog-body input:not([type=checkbox]),.dialog-body select{width:100%;height:40px}.dialog-footer{padding:18px 26px;border-top:1px solid var(--line);display:flex;justify-content:flex-end;gap:10px}.help{font-size:12px;color:var(--muted);line-height:1.8;background:var(--canvas);padding:12px;border-radius:6px}.permissions{display:grid;grid-template-columns:1fr 1fr;gap:12px;font-size:12px}.columns-dialog{width:360px}.columns-dialog label{display:flex;gap:10px;align-items:center}.toast{position:fixed;bottom:35px;left:50%;transform:translateX(-50%);background:#3d3340;color:white;padding:11px 22px;border-radius:7px;box-shadow:0 8px 30px #332b3320;font-size:13px;z-index:10}.toast[hidden]{display:none}.sort{border:0;background:none;padding:0;font:inherit;color:inherit}.sort:hover{background:none}.hide-col{display:none}.danger{color:var(--red)!important}.notice{font-size:12px;color:var(--brand-dark);background:var(--tint);padding:12px;border-radius:6px}
/* Desktop workbench: controls stay compact; wide datasets scroll horizontally. */
body{font-size:12px;background:#f3f4f5;color:#30353b;min-width:1000px}
.app{grid-template-columns:156px minmax(0,1fr);min-height:560px}
.sidebar{padding:0;background:#f7f8f9;border-right:1px solid #d6d9de}
.brand{height:44px;padding:0 12px;gap:8px;background:var(--brand);color:white;border-bottom:1px solid transparent}
.brand b{color:white;font-size:24px}.brand strong{font-size:15px}.brand small{display:none}
.section-label{padding:12px 12px 5px;font-size:11px;letter-spacing:0;color:#808791}
.nav{border-radius:0;margin:0;padding:8px 12px;gap:8px;font-size:12px;height:34px}
.nav.active{background:#eee5ec;color:var(--brand-dark);border-left:0;padding-left:12px}
.nav.active:after{display:none}.nav .nav-index{font-size:10px}.sidebar-bottom{padding:10px 8px}.sidebar-bottom p{display:none}.demo-badge{font-size:10px;padding:2px 4px}.sidebar-bottom button{font-size:11px}
.topbar{height:44px;padding:0 14px;background:var(--brand);color:white;border-bottom:1px solid transparent}.breadcrumb,.breadcrumb span,.profile b{color:white}.breadcrumb{font-size:12px}.profile{gap:16px;font-size:11px}.avatar{display:none}
.content{padding:0 7px 0;gap:0}.page-heading{height:39px;min-height:39px;padding:0 4px;border-bottom:1px solid #dce0e4;gap:10px}.page-heading p,.eyebrow{display:none}h1{font-size:13px;letter-spacing:0;font-weight:600}.heading-actions{gap:6px}
button,input,select{border-radius:2px;font-size:12px}button{padding:3px 10px;height:27px}input,select{height:26px;padding:0 7px}input[type=checkbox]{height:13px;width:13px;margin:0}button.text{height:24px;padding:1px 6px}
.workspace{border:1px solid #d6d9de;border-top:0;border-radius:0}.tabs{min-height:31px;height:31px;flex-shrink:0;margin:0;padding:0 9px;gap:3px;background:#f2f3f5;border-bottom:1px solid #dce0e4}.tab{height:31px;padding:0 13px;font-size:12px;color:#746b78;border-right:1px solid #e3e5e8}.tab.active{background:white}.tab.active:after{height:2px}.tab small{font-size:10px;padding:0 4px;background:none}
.filters{padding:7px 9px;gap:9px;align-items:center;flex-wrap:wrap;background:#ffffff}.filters .field{flex-direction:row;align-items:center;gap:5px}.filters .field label{font-size:12px;color:#626970;white-space:nowrap}.filters .field select{width:112px}.search-field{flex:none;max-width:none}.search-field input{width:235px}
.toolbar{padding:5px 9px;border-top:1px solid #e5e7eb;gap:7px;min-height:36px;background:#fafafa;flex-wrap:wrap}.toolbar-left,.toolbar-right{gap:6px}.toolbar button{font-size:12px;padding:2px 9px;height:25px}.toolbar-left>span{font-size:11px;border-right:1px solid #dce0e4;padding-right:9px;margin-right:3px}
.table-scroll{border-top:1px solid #d6d9de}table{font-size:12px;width:max-content;min-width:100%}th{height:29px;font-size:12px;padding:0 9px;background:#f0f2f4;color:#59616b;font-weight:500;border-right:1px solid #e0e3e7;border-bottom:1px solid #d4d8dd}td{height:36px;padding:2px 9px;border-right:1px solid #e9ebee;border-bottom:1px solid #e9ebee;color:#343a40}.compact td{height:28px;padding:1px 9px}
th:first-child,td:first-child{width:30px;min-width:30px;padding:0 8px;position:sticky;left:0;background:white;z-index:1}th:first-child{background:#f0f2f4;z-index:3}.row-number{color:#9299a1;text-align:right;font-size:11px;min-width:34px}thead th{z-index:2}th:last-child{z-index:3;background:#f0f2f4}td:last-child,th:last-child{box-shadow:-3px 0 4px #0000000a;border-left:1px solid #dce0e4}
tbody tr:nth-child(even) td{background:#fafbfc}tbody tr:hover td{background:#f0f2f4}tbody tr.selected td{background:#f3eaf1}.entity{gap:0}.entity-mark,.entity small,.compact .entity-mark{display:none}.entity-name{margin:0;font-size:12px;font-weight:500}.pill{font-size:11px;padding:0 5px;border-radius:2px;line-height:19px}.tag{background:none;padding:0;font-size:12px;color:#60666e}.date{font-size:12px}.subline{display:inline;font-size:10px;margin-left:6px}.mono{color:#69727d;font-size:11px}.muted{color:#7c838c}.row-action{padding:0 5px!important;height:23px!important}.pagination{height:35px;min-height:35px;flex-shrink:0;padding:3px 9px;background:#f7f8f9;font-size:11px}.pagination button,.pagination select{height:24px}.pagination-controls{gap:6px}.current-page{padding:2px 8px}.statusbar{height:23px;min-height:23px;align-items:center;padding:0 5px;font-size:10px}
.drawer{margin:auto;width:620px;max-width:90vw;height:auto;max-height:90vh;border-radius:3px}.dialog-head{padding:11px 15px;background:#f5f6f7}.dialog-head h2{font-size:14px}.dialog-head p{font-size:10px;margin-top:2px}.dialog-body{padding:15px;gap:13px}.drawer .dialog-body{display:grid;grid-template-columns:1fr 1fr}.drawer .dialog-body>.help{grid-column:1/-1}.dialog-body input:not([type=checkbox]),.dialog-body select{height:29px}.dialog-body .field{gap:5px}.dialog-footer{padding:9px 15px}.permissions{grid-template-columns:1fr;gap:6px}.columns-dialog{border-radius:3px}.help{border-radius:2px;font-size:11px}
</style>
</head>
<body>
<div class="app">
<aside class="sidebar"><div class="brand"><b>29</b><div><strong>供应链</strong><small>COMMERCE WORKSPACE</small></div></div><div class="section-label">店铺工作台</div><nav aria-label="店铺工作台"><button class="nav active" data-page="stores"><span class="nav-index">01</span>店铺管理</button><button class="nav" data-page="accounts"><span class="nav-index">02</span>子账号管理</button></nav><div class="section-label">超级管理</div><nav aria-label="超级管理"><button class="nav" data-page="superstores"><span class="nav-index">03</span>超级店铺管理</button><button class="nav" data-page="users"><span class="nav-index">04</span>超级用户管理</button><button class="nav" data-page="oem"><span class="nav-index">05</span>OEM 贴牌管理</button></nav><div class="sidebar-bottom"><span class="demo-badge">桌面工作台 · 演示</span><p>专注每一家店铺的日常运营<br>本地交互演示 · 数据均为模拟</p><button class="text" id="designNotes">界面设计说明</button></div></aside>
<div class="shell"><header class="topbar"><div class="breadcrumb">管理工作空间 <span>/</span><span id="crumb">店铺管理</span></div><div class="profile"><span>账户积分 <b>9,759</b></span><span class="avatar">管</span><span>管理员</span></div></header>
<main class="content"><section class="page-heading"><div><div class="eyebrow" id="eyebrow">STORE MANAGEMENT</div><h1 id="pageTitle">店铺管理</h1><p id="pageDescription"></p></div><div class="heading-actions"><button id="refresh">刷新列表</button><button class="primary" id="add">添加店铺</button></div></section>
<section class="workspace" aria-label="管理列表"><div class="tabs" id="tabs"></div><form class="filters" id="filterForm"><div class="field search-field"><label for="keyword">关键词</label><input id="keyword" maxlength="100" placeholder="搜索店铺名称 / 店铺 ID"></div><div class="field"><label for="groupFilter">所属分组</label><select id="groupFilter"></select></div><div class="field"><label for="statusFilter">状态</label><select id="statusFilter"><option value="">全部状态</option><option>正常</option><option>即将到期</option><option>已过期</option></select></div><button class="primary" type="submit">查询</button><button type="button" id="reset">重置</button></form>
<div class="toolbar"><div class="toolbar-left"><span id="selection" class="muted">选择记录后可批量操作</span><button id="batch" disabled>批量续期</button><button id="detect" disabled>检测选中</button><button id="login" disabled>登录选中</button></div><div class="toolbar-right"><span class="muted" id="resultCount"></span><button id="density" aria-pressed="true">舒适视图</button><button id="columnSettings">列设置</button></div></div>
<div class="table-scroll"><table id="table" class="compact"><thead id="thead"></thead><tbody id="tbody"></tbody></table></div><footer class="pagination"><span id="range"></span><div class="pagination-controls"><label for="pageSize">每页</label><select id="pageSize"><option>20</option><option selected>50</option><option>100</option></select><button id="prev" aria-label="上一页">上一页</button><span class="current-page" id="pageNumber">1</span><button id="next" aria-label="下一页">下一页</button></div></footer></section>
<footer class="statusbar"><span id="loadStatus">数据已就绪 · 本地演示</span><span>29 供应链 / 管理工作台</span></footer></main></div></div>
<dialog class="drawer" id="editor"><form id="editForm"><header class="dialog-head"><div><h2 id="editorTitle"></h2><p>修改仅在本次演示中生效</p></div><button type="button" class="text" data-close="editor">关闭</button></header><div class="dialog-body" id="editorFields"></div><footer class="dialog-footer"><button type="button" data-close="editor">取消</button><button class="primary" type="submit">保存</button></footer></form></dialog>
<dialog class="columns-dialog" id="columns"><header class="dialog-head"><h2>显示的列</h2><button class="text" data-close="columns">完成</button></header><div class="dialog-body" id="columnOptions"></div></dialog>
<dialog id="details" style="width:520px;max-width:95vw"><header class="dialog-head"><h2 id="detailTitle"></h2><button class="text" data-close="details">关闭</button></header><div class="dialog-body" id="detailBody"></div></dialog><div class="toast" role="status" id="toast" hidden></div>
<script>
'use strict';
const $=id=>document.getElementById(id);
const escapeHtml=value=>String(value??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const names=['乐乐生活馆','清麦日用百货','栖木家居旗舰店','佐薇服饰小铺','青禾优选','云朵母婴生活','拾光好物集合店','橙意食品专营店','木子家居日用','浅湾服饰店','山野户外生活','简物生活旗舰店','暖晨家纺店','知味食品小铺','白露日用百货','云间服饰集合店','小满优选','春序家居店','鹿鸣母婴店','青山好物','星野百货','暮色生活馆','沐光服饰','初禾日用品'];
const stores=Array.from({length:72},(_,i)=>({name:names[i%names.length]+(i>=24?" · "+(Math.floor(i/24)+1)+"店":""),i})).map(({name,i})=>({id:1001+i,name,code:'wx'+(316129135100+i*74031).toString(16)+'a'+i,group:['精选店铺','服饰鞋包','生活百货'][i%3],account:['运营一组','运营二组','未分配'][i%3],owner:'demo_owner_'+(i%4+1),phone:'138****'+String(1200+i),status:i%7===3?'已过期':i%5===2?'即将到期':'正常',expires:i%7===3?'2026-10-20':i%5===2?'2026-11-02':'2027-05-09',note:i%3===0?'重点运营店铺':'',permissions:['店铺列表','订单管理'],created:'2026-08-14 15:16',login:i%7===3?'未登录':'登录成功',api:i%4===0?'未购买':'已开通',permanent:i%9===0?'是':'否',secondary:i%6===0?'已启用':'未启用'}));
const data={stores,superstores:stores.map(r=>({...r,id:r.id+3000})),accounts:['林小夏','陈晓','周宁','许安','李可','宋悦'].map((name,i)=>({id:2001+i,name,code:'operator_'+(i+1),phone:'139****'+(2300+i),group:['运营','客服','仓储'][i%3],status:i===4?'已停用':'正常',account:'主账号 · 演示企业',note:'',permissions:['店铺列表','订单管理']})),users:Array.from({length:18},(_,i)=>({id:3001+i,name:['青禾供应链','云飞商贸','开兴科技','花下海','众店联源','微擎科技'][i%6]+(i>5?' '+(Math.floor(i/6)+1):''),code:'user_'+(801+i),phone:'137****'+(5600+i),group:i%4===0?'代理商':'主账户',status:i===5?'已停用':'正常',company:['青禾商贸有限公司','云飞供应链有限公司','开兴网络科技'][i%3],points:300+i*20,balance:(140+i*37.5).toFixed(2),fee:'2.50',monthly:30,returnFee:'3.00',agent:['云飞','大牛','花下海'][i%3],type:'普通用户',permissions:['商品采集','在线寄件'],note:''})),oem:['桃花源供应链','店速通','开茂科技','枫谷拍单','洋总代理','臻和网络','贝塔一体仓','佳俊商贸'].map((name,i)=>({id:5001+i,name,code:'26'+(701+i)+'TW'+i,group:'品牌合作',status:'正常',account:'demo_brand_'+(i+1),phone:'136****'+(7400+i),note:'',permissions:[]}))};
const configs={stores:{title:'店铺管理',en:'STORE MANAGEMENT',desc:'集中管理店铺、账号与授权,让日常运营井然有序。',add:'添加店铺',batch:'批量续期',search:'搜索店铺名称 / 店铺 ID',columns:[['name','店铺信息'],['group','所属分组'],['account','子账号'],['status','授权状态'],['expires','到期时间'],['note','备注']],groups:['精选店铺','服饰鞋包','生活百货']},superstores:{title:'超级店铺管理',en:'STORE ADMINISTRATION',desc:'跨账号查看全部店铺,统一管理店铺归属与授权状态。',add:'添加店铺',batch:'批量检测',search:'搜索店铺 / 所属账号 / 店铺 ID',columns:[['name','店铺信息'],['owner','所属账号'],['group','店铺分组'],['account','子账号'],['status','授权状态'],['expires','到期时间']],groups:['精选店铺','服饰鞋包','生活百货']},accounts:{title:'子账号管理',en:'TEAM & ACCESS',desc:'为团队分配独立账号,以清晰的访问权限协同管理店铺。',add:'添加子账号',batch:'批量启用',search:'搜索昵称 / 登录账号',columns:[['name','用户信息'],['phone','登录手机号'],['group','角色分组'],['status','账号状态'],['permissions','可访问页面'],['account','所属主账号']],groups:['运营','客服','仓储']},users:{title:'超级用户管理',en:'USER ADMINISTRATION',desc:'统一管理用户、功能权限与账户信息。',add:'添加用户',batch:'批量启用',search:'搜索用户 / 手机号 / 公司',columns:[['name','用户信息'],['group','用户分组'],['company','公司'],['points','积分'],['balance','快递余额'],['fee','每单快递费'],['status','账号状态']],groups:['主账户','代理商']},oem:{title:'OEM 贴牌管理',en:'BRAND PARTNERS',desc:'管理合作品牌标识与主体账号,建立统一的品牌工作空间。',add:'添加贴牌',batch:'批量启用',search:'搜索贴牌名称 / 贴牌编码',columns:[['name','贴牌名称'],['code','贴牌编码'],['account','主体账号'],['status','状态'],['assets','品牌素材']],groups:['品牌合作']}};
configs.stores.columns=[['name','店铺名称'],['code','店铺 ID'],['created','添加时间'],['expires','到期时间'],['status','授权状态'],['group','分组'],['account','子账号'],['phone','子账号手机号'],['login','登录状态'],['api','API 购买'],['note','备注']];
configs.superstores.columns=[['name','店铺名称'],['code','店铺 ID / 登录号码'],['owner','所属账号'],['account','子账号'],['group','店铺分组'],['login','登录状态'],['status','授权状态'],['api','API 购买'],['secondary','二级超管'],['permanent','永久'],['note','备注']];
configs.accounts.columns=[['name','用户昵称'],['code','登录账号'],['phone','手机号'],['group','分组'],['account','所属主账号'],['status','状态'],['permissions','可访问页面'],['note','备注']];
configs.users.columns=[['name','用户昵称'],['phone','手机 / 登录账号'],['group','分组'],['company','公司'],['type','账户类型'],['permissions','功能权限'],['agent','代理名称'],['points','积分'],['monthly','月费耗点'],['balance','快递余额'],['fee','每单快递费'],['returnFee','每单退货费'],['status','状态']];
configs.oem.columns=[['code','贴牌编码'],['name','贴牌名称'],['account','主体账号'],['assets','图标'],['assetsTitle','题图'],['status','状态'],['note','备注']];
let current='stores',tab='',page=1,query='',group='',status='',sortAsc=true,editingId=null,selected=new Set(),hiddenColumns=new Set(),toastTimer;
function isStore(){return current==='stores'||current==='superstores'}
function filtered(){return data[current].filter(r=>(!query||Object.values(r).join(' ').toLowerCase().includes(query))&&(!group||r.group===group)&&(!status||r.status===status)&&(!tab||r.status===tab)).sort((a,b)=>sortAsc?a.name.localeCompare(b.name,'zh-CN'):b.name.localeCompare(a.name,'zh-CN'))}
function visibleRows(){const rows=filtered();return rows.slice((page-1)*Number($('pageSize').value),page*Number($('pageSize').value))}
function showToast(message){$('toast').textContent=message;$('toast').hidden=false;clearTimeout(toastTimer);toastTimer=setTimeout(()=>$('toast').hidden=true,3200)}
function renderTabs(){const tabs=isStore()?[['','全部店铺'],['正常','授权正常'],['即将到期','即将到期'],['已过期','已过期']]:[['','全部'+(current==='oem'?'品牌':'账号')],['正常','正常'],['已停用','已停用']];$('tabs').innerHTML=tabs.map(([value,label])=>`<button class="tab ${tab===value?'active':''}" data-tab="${value}" aria-pressed="${tab===value}">${label}<small>${data[current].filter(r=>!value||r.status===value).length}</small></button>`).join('')}
function cell(r,key){const v=escapeHtml(r[key]);switch(key){case'name':return `<div class="entity"><span class="entity-mark ${r.id%3===0?'alt':''}">${escapeHtml(r.name.slice(0,1))}</span><div><div class="entity-name">${v}</div><small>${escapeHtml(r.code)}</small></div></div>`;case'login':return `<span style="color:${r.login==='登录成功'?'var(--green)':'var(--muted)'}">${v}</span>`;case'status':return `<span class="pill ${r.status==='即将到期'?'warn':r.status==='正常'?'':'error'}">${v==='正常'&&isStore()?'授权正常':v}</span>`;case'group':return `<span class="tag">${v}</span>`;case'expires':return `<span class="date">${v}</span>${r.status==='即将到期'?'<div class="subline" style="color:var(--amber)">5 天后到期</div>':''}`;case'permissions':return `<span class="muted">${escapeHtml(r.permissions.join('、'))||'尚未分配'}</span>`;case'assetsTitle':case'assets':return '<span class="muted">暂未上传</span>';case'balance':case'fee':case'returnFee':return `<span class="date">¥ ${v}</span>`;case'code':case'owner':return `<span class="mono">${v}</span>`;case'note':return `<span class="muted">${v||'—'}</span>`;default:return v||'<span class="muted">—</span>'}}
function renderTable(){const cfg=configs[current],rows=filtered(),size=Number($('pageSize').value);page=Math.max(1,Math.min(page,Math.ceil(rows.length/size)||1));const visible=visibleRows(),cols=cfg.columns.filter(([key])=>!hiddenColumns.has(key));$('thead').innerHTML=`<tr><th><input type="checkbox" id="selectAll" aria-label="选择当前页全部记录" ${visible.length&&visible.every(r=>selected.has(r.id))?'checked':''}></th><th class="row-number">序号</th>${cols.map(([key,label])=>`<th>${key==='name'?`<button class="sort" id="sortName">${label} ${sortAsc?'↑':'↓'}</button>`:label}</th>`).join('')}<th>操作</th></tr>`;$('tbody').innerHTML=visible.length?visible.map((r,index)=>`<tr class="${selected.has(r.id)?'selected':''}"><td><input type="checkbox" data-select="${r.id}" aria-label="选择${escapeHtml(r.name)}" ${selected.has(r.id)?'checked':''}></td><td class="row-number">${(page-1)*size+index+1}</td>${cols.map(([key])=>`<td>${cell(r,key)}</td>`).join('')}<td>${isStore()?`<button class="text row-action" data-store-action="enter" data-id="${r.id}">进入</button><button class="text row-action" data-store-action="renew" data-id="${r.id}">续期</button>`:""}<button class="text row-action" data-detail="${r.id}">${isStore()?'查看':'详情'}</button><button class="text row-action" data-edit="${r.id}">编辑</button></td></tr>`).join(''):`<tr><td class="empty" colspan="${cols.length+3}">没有匹配的记录,请调整筛选条件或重置。</td></tr>`;const all=$('selectAll');all.indeterminate=visible.some(r=>selected.has(r.id))&&!visible.every(r=>selected.has(r.id));$('selection').textContent=selected.size?`已选择 ${selected.size} 项`:'选择记录后可批量操作';$('selection').className=selected.size?'selection':'muted';$('batch').disabled=!selected.size;$('detect').disabled=$('login').disabled=!selected.size;$('resultCount').textContent=`共 ${rows.length} 条记录`;$('range').textContent=rows.length?`显示 ${(page-1)*size+1}–${Math.min(page*size,rows.length)} 条,共 ${rows.length} 条`:'共 0 条记录';$('pageNumber').textContent=`${page} / ${Math.ceil(rows.length/size)||1}`;$('prev').disabled=page===1;$('next').disabled=page*size>=rows.length}
function render(){renderTabs();renderTable()}
function switchPage(key){current=key;tab='';page=1;query='';group='';status='';selected.clear();hiddenColumns.clear();const cfg=configs[key];$('detect').hidden=$('login').hidden=!isStore();document.querySelectorAll('[data-page]').forEach(b=>{b.classList.toggle('active',b.dataset.page===key);b.setAttribute('aria-current',b.dataset.page===key?'page':'false')});$('crumb').textContent=$('pageTitle').textContent=cfg.title;$('eyebrow').textContent=cfg.en;$('pageDescription').textContent=cfg.desc;$('add').textContent=cfg.add;$('batch').textContent=cfg.batch;$('keyword').value='';$('keyword').placeholder=cfg.search;$('groupFilter').innerHTML='<option value="">全部分组</option>'+cfg.groups.map(g=>`<option>${g}</option>`).join('');$('statusFilter').innerHTML='<option value="">全部状态</option>'+(isStore()?['正常','即将到期','已过期']:['正常','已停用']).map(s=>`<option>${s}</option>`).join('');render()}
function field(label,name,value='',required=false,type='text'){return `<div class="field"><label for="edit_${name}">${label}${required?' <span style="color:var(--brand)">*</span>':''}</label><input id="edit_${name}" name="${name}" type="${type}" value="${escapeHtml(value)}" maxlength="100" ${required?'required':''}></div>`}
function openEditor(id=null){editingId=id;const r=data[current].find(r=>r.id===id)||{},cfg=configs[current];$('editorTitle').textContent=id?'编辑'+(isStore()?'店铺':current==='oem'?'贴牌':'用户'):cfg.add;let fields=field(isStore()?'店铺名称':current==='oem'?'贴牌名称':'用户昵称','name',r.name,true)+field(isStore()?'店铺 ID':current==='oem'?'贴牌编码':'登录账号','code',r.code,true);if(current==='accounts'||current==='users')fields+=field('手机号','phone',r.phone,true)+field(id?'新密码(留空则不修改)':'密码','password','',!id,'password');fields+=`<div class="field"><label for="edit_group">所属分组</label><select name="group" id="edit_group">${cfg.groups.map(g=>`<option ${r.group===g?'selected':''}>${g}</option>`).join('')}</select></div>`;if(isStore()||current==='oem')fields+=field(current==='oem'?'主体账号':'子账号','account',r.account,current==='oem');if(current==='users')fields+=field('公司','company',r.company);if(!isStore())fields+=`<div class="field"><label for="edit_status">账号状态</label><select name="status" id="edit_status"><option ${r.status!=='已停用'?'selected':''}>正常</option><option ${r.status==='已停用'?'selected':''}>已停用</option></select></div>`;if(current==='accounts'||current==='users')fields+=`<div class="field"><label>可访问页面</label><div class="permissions">${['店铺列表','订单管理','商品采集','在线寄件','评分考核','税务申报'].map(p=>`<label><input type="checkbox" name="permission" value="${p}" ${(r.permissions||[]).includes(p)?'checked':''}>${p}</label>`).join('')}</div></div>`;fields+=field('备注','note',r.note);if(isStore()&&!id)fields+='<div class="help">此处演示手动添加流程。正式系统可在此接入扫码授权;新记录默认使用演示授权。</div>';$('editorFields').innerHTML=fields;$('editor').showModal()}
document.querySelectorAll('[data-page]').forEach(b=>b.onclick=()=>switchPage(b.dataset.page));
$('filterForm').onsubmit=e=>{e.preventDefault();query=$('keyword').value.trim().toLowerCase();group=$('groupFilter').value;status=$('statusFilter').value;page=1;selected.clear();renderTable()};
$('reset').onclick=()=>{query=group=status=tab='';page=1;selected.clear();$('keyword').value=$('groupFilter').value=$('statusFilter').value='';render()};
$('tabs').onclick=e=>{const b=e.target.closest('[data-tab]');if(b){tab=b.dataset.tab;page=1;selected.clear();renderTabs();renderTable()}};
$('prev').onclick=()=>{page--;renderTable()};$('next').onclick=()=>{page++;renderTable()};$('pageSize').onchange=()=>{page=1;renderTable()};
$('table').onchange=e=>{if(e.target.id==='selectAll')visibleRows().forEach(r=>e.target.checked?selected.add(r.id):selected.delete(r.id));else if(e.target.dataset.select){const id=Number(e.target.dataset.select);e.target.checked?selected.add(id):selected.delete(id)}renderTable()};
$('table').onclick=e=>{const action=e.target.closest('[data-store-action]');if(action){const r=data[current].find(r=>r.id===Number(action.dataset.id));if(action.dataset.storeAction==='renew'){r.status='正常';r.expires='2027-05-09';render();showToast('已模拟续期:'+r.name)}else{showToast('模拟进入店铺:'+r.name+'(未连接真实店铺)')}return}const edit=e.target.closest('[data-edit]'),detail=e.target.closest('[data-detail]');if(edit)openEditor(Number(edit.dataset.edit));if(detail){const r=data[current].find(r=>r.id===Number(detail.dataset.detail));$('detailTitle').textContent=r.name;$('detailBody').innerHTML=configs[current].columns.map(([key,label])=>`<div><div class="muted" style="font-size:11px;margin-bottom:6px">${label}</div>${cell(r,key)}</div>`).join('')+'<div class="notice">此为本地演示记录,不会连接店铺或读取真实账号。</div>';$('details').showModal()}if(e.target.id==='sortName'){sortAsc=!sortAsc;renderTable()}};
$('density').onclick=()=>{const compact=$('table').classList.toggle('compact');$('density').textContent=compact?'舒适视图':'紧凑视图';$('density').setAttribute('aria-pressed',compact)};
$('columnSettings').onclick=()=>{$('columnOptions').innerHTML=configs[current].columns.map(([key,label])=>`<label><input type="checkbox" value="${key}" ${hiddenColumns.has(key)?'':'checked'} ${key==='name'?'disabled':''}>${label}${key==='name'?'(固定)':''}</label>`).join('');$('columns').showModal()};
$('columnOptions').onchange=e=>{e.target.checked?hiddenColumns.delete(e.target.value):hiddenColumns.add(e.target.value);renderTable()};
$('add').onclick=()=>openEditor();document.querySelectorAll('[data-close]').forEach(b=>b.onclick=()=>$(b.dataset.close).close());
$('editForm').onsubmit=e=>{e.preventDefault();const values=new FormData(e.target),name=String(values.get('name')).trim(),code=String(values.get('code')).trim();if(!name||!code){showToast('名称和编号不能为空');return}if(data[current].some(r=>r.code===code&&r.id!==editingId)){showToast('该编号已存在,请使用其他编号');return}const existing=data[current].find(r=>r.id===editingId);const record={...(existing||{id:Date.now(),status:'正常',expires:'2027-05-09',owner:'demo_owner_1',points:0,balance:'0.00',fee:'2.50'}),name,code,group:values.get('group'),note:values.get('note'),permissions:values.getAll('permission')};for(const key of ['phone','account','company','status'])if(values.has(key))record[key]=values.get(key);if(existing)Object.assign(existing,record);else data[current].unshift(record);$('editor').close();render();showToast(editingId?'已保存修改(演示)':'已添加记录(演示)')};
$('batch').onclick=()=>{let count=selected.size;data[current].filter(r=>selected.has(r.id)).forEach(r=>{if(current!=='superstores'){r.status='正常';if(current==='stores')r.expires='2027-05-09'}});selected.clear();render();showToast(current==='superstores'?`已完成 ${count} 家店铺的模拟检测`:`已更新 ${count} 条演示记录`)};
$('detect').onclick=()=>{showToast('已模拟检测 '+selected.size+' 家店铺;登录状态已显示在表格中')};
$('login').onclick=()=>{data[current].filter(r=>selected.has(r.id)).forEach(r=>r.login='登录成功');renderTable();showToast('已模拟登录 '+selected.size+' 家店铺')};
$('refresh').onclick=()=>{render();$('loadStatus').textContent='已刷新 · '+new Date().toLocaleTimeString('zh-CN')+' · 本地演示';showToast('列表已刷新')};
$('designNotes').onclick=()=>{$('detailTitle').textContent='为桌面工作流而设计';$('detailBody').innerHTML='<div class="notice">品牌主色 #98638f · 梅紫</div><p>五个页面共享导航、筛选栏、表格和分页布局,降低切换页面的学习成本。</p><p>店铺名称、ID、归属、授权和登录状态分别列示,常用操作直接放在工具栏与固定操作列;编辑使用居中对话框。</p><p>默认紧凑行高 28px,舒适行高 36px;默认每页 50 条。表头与操作列固定,适合长列表与横向浏览。</p><p>后续 WPF 可对应 Grid、DataGrid、ResourceDictionary 和 Dialog。此 HTML 仅演示交互,不包含权限校验、真实登录、支付续期或 API 接入。</p>';$('details').showModal()};
switchPage('stores');
console.assert(escapeHtml('<script>')==='&lt;script&gt;','HTML escaping');
console.assert(filtered().length===72,'Initial store count');
query='不存在的店铺';console.assert(filtered().length===0,'Empty search');query='';
status='已过期';console.assert(filtered().every(r=>r.status==='已过期'),'Status filter');status='';
console.assert(new Set(stores.map(r=>r.id)).size===stores.length,'Unique record IDs');
</script>
</body>
</html>