95 lines
38 KiB
HTML
95 lines
38 KiB
HTML
<!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=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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>')==='<script>','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>
|