docs(design): 单据状态徽章图标化 + 移动出入库列表补 KPI 卡/新增按钮进搜索行

- atoms.css 徽章新增两变体并登记 index.html:.bi 代表性图标替代圆点(桌面),
  .ico 纯图标紧凑徽章(移动卡片,22px 圆)
- 图标映射:草稿=文件 待审核=时钟 已审核=勾 已拒绝=叉 退单=撤回箭头(i-undo 新增
  lucide undo-2,原型/官网 sprite 两端同步) 待定价=价签
- 桌面 stock-in/out-list 表格与抽屉状态徽章圆点 → 图标
- 移动 m-stock-*-list:卡片徽章改纯图标横排(解决竖排文字徽章过大);详情 sheet
  用图标+文字徽章;FAB 移除,「新增」primary 按钮放搜索框旁;补 2×2 KPI 卡
  (近30天笔数/金额 + 待审核/草稿 点击筛选,对齐桌面滚动窗口径)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
wangjia
2026-07-04 09:38:58 +08:00
parent 12952ea9ae
commit 84e0653443
8 changed files with 56 additions and 19 deletions
+5
View File
@@ -71,6 +71,11 @@ svg{fill:none; stroke:currentColor;}
/* ---- 状态徽章 ---- */
.badge{display:inline-flex; align-items:center; gap:5px; height:22px; padding:0 9px; border-radius:var(--r-pill); font-size:var(--fs-sm); font-weight:600;}
.badge::before{content:""; width:6px; height:6px; border-radius:50%; background:currentColor;}
/* 徽章图标化(2026-07-04):.bi=代表性图标替代圆点(桌面);.ico=纯图标紧凑徽章(移动卡片) */
.badge.bi::before, .badge.ico::before{content:none;}
.badge .ic{width:12px; height:12px; stroke-width:2.2;}
.badge.ico{width:22px; padding:0; justify-content:center; gap:0;}
.badge.ico .ic{width:13px; height:13px;}
.b-在售{background:var(--success-bg); color:var(--success);}
.b-缺货{background:var(--danger-bg); color:var(--danger);}
.b-预警{background:var(--warn-bg); color:var(--warn);}
+2
View File
@@ -377,6 +377,8 @@
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap"><span style="width:160px;flex:none;font-size:var(--fs-xs);color:var(--muted);font-weight:600">授权兑换券</span><span class="badge b-未使用">未使用</span><span class="badge b-已兑换">已兑换</span><span class="badge b-作废">作废</span></div>
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap"><span style="width:160px;flex:none;font-size:var(--fs-xs);color:var(--muted);font-weight:600">账单结清 · 收付</span><span class="badge b-未结清">未结清</span><span class="badge b-已结清">已结清</span><span class="badge b-收款">收款</span><span class="badge b-付款">付款</span></div>
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap"><span style="width:160px;flex:none;font-size:var(--fs-xs);color:var(--muted);font-weight:600">进价待定 · 调货暂估</span><span class="badge b-待定价">待定价</span><span class="pending-price">待定价</span><span style="font-size:var(--fs-xs);color:var(--faint)">(入库进价留空/0,确认进价后回填)</span></div>
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap"><span style="width:160px;flex:none;font-size:var(--fs-xs);color:var(--muted);font-weight:600">图标徽章 .bi · 图标替代圆点</span><span class="badge bi b-草稿"><svg class="ic"><use href="#i-ic06"/></svg>草稿</span><span class="badge bi b-待审核"><svg class="ic"><use href="#i-ic04"/></svg>待审核</span><span class="badge bi b-已通过"><svg class="ic"><use href="#i-check"/></svg>已审核</span><span class="badge bi b-已驳回"><svg class="ic"><use href="#i-close"/></svg>已拒绝</span><span class="badge bi b-预警"><svg class="ic"><use href="#i-undo"/></svg>部分退单</span><span class="badge bi b-缺货"><svg class="ic"><use href="#i-undo"/></svg>已退单</span><span class="badge bi b-待定价"><svg class="ic"><use href="#i-tag"/></svg>待定价</span></div>
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap"><span style="width:160px;flex:none;font-size:var(--fs-xs);color:var(--muted);font-weight:600">纯图标徽章 .ico · 移动卡片</span><span class="badge ico b-草稿"><svg class="ic"><use href="#i-ic06"/></svg></span><span class="badge ico b-待审核"><svg class="ic"><use href="#i-ic04"/></svg></span><span class="badge ico b-已通过"><svg class="ic"><use href="#i-check"/></svg></span><span class="badge ico b-已驳回"><svg class="ic"><use href="#i-close"/></svg></span><span class="badge ico b-预警"><svg class="ic"><use href="#i-undo"/></svg></span><span class="badge ico b-缺货"><svg class="ic"><use href="#i-undo"/></svg></span><span class="badge ico b-待定价"><svg class="ic"><use href="#i-tag"/></svg></span><span style="font-size:var(--fs-xs);color:var(--faint)">(图标映射:草稿=文件 待审核=时钟 已审核=勾 已拒绝=叉 退单=撤回箭头 待定价=价签)</span></div>
</div>
</div></div>
<div class="pcard"><div class="pcard-h"><h3>KPI 卡</h3><span class="tk">.kpi</span></div><div class="pcard-b">
File diff suppressed because one or more lines are too long
@@ -20,14 +20,22 @@
<div class="m-top" data-m-top data-title="入库管理"></div>
<div class="m-page has-tabs">
<div class="m-scroll">
<div class="m-search"><svg viewBox="0 0 24 24"><use href="#i-search"/></svg><input id="q" placeholder="搜索单号 / 供应商 / 酒名" oninput="state.q=this.value;render()"></div>
<div style="display:flex;gap:10px;align-items:stretch;margin-bottom:10px">
<div class="m-search" style="flex:1;min-width:0"><svg viewBox="0 0 24 24"><use href="#i-search"/></svg><input id="q" placeholder="搜索单号 / 供应商 / 酒名" oninput="state.q=this.value;render()"></div>
<div class="btn primary" style="height:auto;flex:none" onclick="location.href='m-stock-in.html'"><svg viewBox="0 0 24 24"><use href="#i-plus"/></svg>新增</div>
</div>
<div class="m-kpi" style="margin-bottom:10px">
<div class="k" onclick="setSt('全部')"><div class="kl"><svg viewBox="0 0 24 24"><use href="#i-download"/></svg>近30天入库笔数</div><div class="kv">28</div><div class="kd up">▲ 12% 较上期</div></div>
<div class="k"><div class="kl"><svg viewBox="0 0 24 24"><use href="#i-ic47"/></svg>近30天入库金额</div><div class="kv">¥186万</div><div class="kd up">▲ 8.4% 较上期</div></div>
<div class="k" onclick="setSt('待审核')"><div class="kl"><svg viewBox="0 0 24 24"><use href="#i-ic42"/></svg>待审核 · 点击筛选</div><div class="kv">4</div><div class="kd warn">需尽快处理</div></div>
<div class="k" onclick="setSt('草稿')"><div class="kl"><svg viewBox="0 0 24 24"><use href="#i-ic06"/></svg>草稿 · 点击筛选</div><div class="kv">2</div><div class="kd">待提交审核</div></div>
</div>
<div class="m-filters" id="pills"></div>
<div class="m-section" id="cnt">入库单</div>
<div id="list"></div>
</div>
</div>
<nav class="m-tabbar" data-m-tabs data-active="stock-in"></nav>
<div class="m-fab" onclick="location.href='m-stock-in.html'"><svg viewBox="0 0 24 24"><use href="#i-plus"/></svg></div>
<div class="toast" id="toast"></div>
<script src="icons.js"></script>
@@ -48,6 +56,10 @@ const OPS=['全部','王经理','李采购'];
const REVIEWERS=['全部','张主管','王经理'];
const DATES=['全部时间','近 7 天','近 30 天','本月'];
const state={q:'',status:'全部'};
/* ---- 状态代表图标(徽章图标化 2026-07-04:移动纯图标 .ico / 桌面图标+文字 .bi ---- */
const STICON={'草稿':'i-ic06','待审核':'i-ic04','已审核':'i-check','已拒绝':'i-close','部分退单':'i-undo','已退单':'i-undo','待定价':'i-tag'};
function icoB(v){ return `<span class="badge ico b-${v}" title="${v}"><svg class="ic"><use href="#${STICON[v]}"/></svg></span>`; }
function biB(v){ return `<span class="badge bi b-${v}"><svg class="ic"><use href="#${STICON[v]}"/></svg>${v}</span>`; }
/* ---- 详细搜索条件(与 Flutter _AdvSearchDialog 同一字段集,移动为竖排简化版) ---- */
const ADV_DEF={no:'',product:'',batch:'',party:'全部',series:'全部',spec:'全部',op:'全部',reviewer:'全部',status:'全部',date:'全部时间'};
let adv={...ADV_DEF};
@@ -75,7 +87,7 @@ function render(){
L.innerHTML=rows.map(o=>`<a class="m-card" onclick="openOrder('${o.no}')">
<div class="m-row">
<div class="mc-main"><div class="mc-nm">${o.no}</div><div class="mc-sub">${o.party} · ${o.items} 项</div></div>
<div class="mc-right"><span class="badge b-${o.status}">${o.status}</span>${o.ret?`<span class="badge b-${o.ret}">${o.ret}</span>`:''}${o.pending?'<span class="badge b-待定价">待定价</span>':''}<span class="mc-amt">${o.amount}</span></div>
<div class="mc-right"><div style="display:flex;gap:5px">${icoB(o.status)}${o.ret?icoB(o.ret):''}${o.pending?icoB('待定价'):''}</div><span class="mc-amt">${o.amount}</span></div>
<div class="mc-chev"><svg viewBox="0 0 24 24"><use href="#i-chevron-right"/></svg></div>
</div>
<div class="mc-foot"><svg width="13" height="13" viewBox="0 0 24 24" stroke-width="1.9" style="color:var(--muted)"><use href="#i-ic24"/></svg>${o.date} · 经手人 ${o.by}${o.auditor?' · 审核 '+o.auditor:''}</div>
@@ -121,11 +133,11 @@ function actions(o){
return '<div class="btn ghost" style="flex:1;justify-content:center" onclick="closeSheet()">关闭</div>';
}
function openOrder(no){ const o=ORDERS.find(x=>x.no===no);
const pendRow=o.pending?`<div class="drow"><span>成本</span><b><span class="badge b-待定价">含待定价</span></b></div>`:'';
const pendRow=o.pending?`<div class="drow"><span>成本</span><b>${biB('待定价')}</b></div>`:'';
const confirmBtn=(o.pending&&o.status==='已审核')?`<div class="btn primary" style="flex:1;justify-content:center" onclick="openConfirm('${o.no}')"><svg viewBox="0 0 24 24"><use href="#i-edit"/></svg>确认进价</div>`:'';
const lns=o.lines||[];
const lineRows=lns.map((l,i)=>`<div class="drow"${i===lns.length-1?' style="border-bottom:none"':''}><span style="color:var(--text);line-height:1.45">${l.n}<br><span style="color:var(--faint);font-size:var(--fs-xs)">${l.s} · ×${l.q}</span></span><b style="font-family:var(--font-mono)">${l.pend?'<span class="badge b-待定价">待定价</span>':l.a}</b></div>`).join('');
openSheet('入库单详情', `<div class="drow"><span>单号</span><b style="font-family:var(--font-mono)">${o.no}</b></div><div class="drow"><span>供应商</span><b>${o.party}</b></div><div class="drow"><span>单据日期</span><b>${o.date}</b></div><div class="drow"><span>入库员</span><b>${o.by}</b></div><div class="drow"><span>审核员</span><b>${o.auditor||'—'}</b></div><div class="drow"><span>合计金额</span><b style="font-family:var(--font-mono)">${o.amount}</b></div>${pendRow}<div class="drow" style="border-bottom:none"><span>状态</span><b><span class="badge b-${o.status}">${o.status}</span>${o.ret?` <span class="badge b-${o.ret}">${o.ret}</span>`:''}</b></div>
const lineRows=lns.map((l,i)=>`<div class="drow"${i===lns.length-1?' style="border-bottom:none"':''}><span style="color:var(--text);line-height:1.45">${l.n}<br><span style="color:var(--faint);font-size:var(--fs-xs)">${l.s} · ×${l.q}</span></span><b style="font-family:var(--font-mono)">${l.pend?biB('待定价'):l.a}</b></div>`).join('');
openSheet('入库单详情', `<div class="drow"><span>单号</span><b style="font-family:var(--font-mono)">${o.no}</b></div><div class="drow"><span>供应商</span><b>${o.party}</b></div><div class="drow"><span>单据日期</span><b>${o.date}</b></div><div class="drow"><span>入库员</span><b>${o.by}</b></div><div class="drow"><span>审核员</span><b>${o.auditor||'—'}</b></div><div class="drow"><span>合计金额</span><b style="font-family:var(--font-mono)">${o.amount}</b></div>${pendRow}<div class="drow" style="border-bottom:none"><span>状态</span><b>${biB(o.status)}${o.ret?' '+biB(o.ret):''}</b></div>
<div class="pe-card" style="margin-top:14px"><div class="pe-h"><span class="pe-ic"><svg viewBox="0 0 24 24"><use href="#i-ic33"/></svg></span>商品明细 · ${lns.length} 项</div><div class="pe-b" style="padding-top:2px;padding-bottom:2px">${lineRows}</div></div>
${o.pending?'<div style="margin-top:12px;font-size:var(--fs-sm);line-height:1.6;color:var(--warn)">调货「先卖后定价」:进价待定,价格确定后点「确认进价」前向补齐成本与应付。</div>':''}
<div style="display:flex;gap:10px;margin-top:16px">${confirmBtn}${actions(o)}</div>`);
@@ -20,14 +20,22 @@
<div class="m-top" data-m-top data-title="出库管理"></div>
<div class="m-page has-tabs">
<div class="m-scroll">
<div class="m-search"><svg viewBox="0 0 24 24"><use href="#i-search"/></svg><input id="q" placeholder="搜索单号 / 客户 / 酒名" oninput="state.q=this.value;render()"></div>
<div style="display:flex;gap:10px;align-items:stretch;margin-bottom:10px">
<div class="m-search" style="flex:1;min-width:0"><svg viewBox="0 0 24 24"><use href="#i-search"/></svg><input id="q" placeholder="搜索单号 / 客户 / 酒名" oninput="state.q=this.value;render()"></div>
<div class="btn primary" style="height:auto;flex:none" onclick="location.href='m-stock-out.html'"><svg viewBox="0 0 24 24"><use href="#i-plus"/></svg>新增</div>
</div>
<div class="m-kpi" style="margin-bottom:10px">
<div class="k" onclick="setSt('全部')"><div class="kl"><svg viewBox="0 0 24 24"><use href="#i-upload"/></svg>近30天出库笔数</div><div class="kv">35</div><div class="kd up">▲ 9.2% 较上期</div></div>
<div class="k"><div class="kl"><svg viewBox="0 0 24 24"><use href="#i-ic47"/></svg>近30天出库金额</div><div class="kv">¥216万</div><div class="kd up">▲ 6.8% 较上期</div></div>
<div class="k" onclick="setSt('待审核')"><div class="kl"><svg viewBox="0 0 24 24"><use href="#i-ic42"/></svg>待审核 · 点击筛选</div><div class="kv">3</div><div class="kd warn">需尽快处理</div></div>
<div class="k" onclick="setSt('草稿')"><div class="kl"><svg viewBox="0 0 24 24"><use href="#i-ic06"/></svg>草稿 · 点击筛选</div><div class="kv">1</div><div class="kd">待提交审核</div></div>
</div>
<div class="m-filters" id="pills"></div>
<div class="m-section" id="cnt">出库单</div>
<div id="list"></div>
</div>
</div>
<nav class="m-tabbar" data-m-tabs data-active="stock-out"></nav>
<div class="m-fab" onclick="location.href='m-stock-out.html'"><svg viewBox="0 0 24 24"><use href="#i-plus"/></svg></div>
<div class="toast" id="toast"></div>
<script src="icons.js"></script>
@@ -47,6 +55,10 @@ const OPS=['全部','王经理','李销售'];
const REVIEWERS=['全部','张主管','王经理'];
const DATES=['全部时间','近 7 天','近 30 天','本月'];
const state={q:'',status:'全部'};
/* ---- 状态代表图标(徽章图标化 2026-07-04:移动纯图标 .ico / 桌面图标+文字 .bi ---- */
const STICON={'草稿':'i-ic06','待审核':'i-ic04','已审核':'i-check','已拒绝':'i-close','部分退单':'i-undo','已退单':'i-undo','待定价':'i-tag'};
function icoB(v){ return `<span class="badge ico b-${v}" title="${v}"><svg class="ic"><use href="#${STICON[v]}"/></svg></span>`; }
function biB(v){ return `<span class="badge bi b-${v}"><svg class="ic"><use href="#${STICON[v]}"/></svg>${v}</span>`; }
/* ---- 详细搜索条件(与 Flutter _AdvancedSearchDialog 同一字段集,移动为竖排简化版) ---- */
const ADV_DEF={no:'',product:'',batch:'',party:'全部',series:'全部',spec:'全部',op:'全部',reviewer:'全部',status:'全部',date:'全部时间'};
let adv={...ADV_DEF};
@@ -74,7 +86,7 @@ function render(){
L.innerHTML=rows.map(o=>`<a class="m-card" onclick="openOrder('${o.no}')">
<div class="m-row">
<div class="mc-main"><div class="mc-nm">${o.no}</div><div class="mc-sub">${o.party} · ${o.items} 项</div></div>
<div class="mc-right"><span class="badge b-${o.status}">${o.status}</span>${o.ret?`<span class="badge b-${o.ret}">${o.ret}</span>`:''}${o.pending?'<span class="badge b-待定价">待定价</span>':''}<span class="mc-amt">${o.amount}</span></div>
<div class="mc-right"><div style="display:flex;gap:5px">${icoB(o.status)}${o.ret?icoB(o.ret):''}${o.pending?icoB('待定价'):''}</div><span class="mc-amt">${o.amount}</span></div>
<div class="mc-chev"><svg viewBox="0 0 24 24"><use href="#i-chevron-right"/></svg></div>
</div>
<div class="mc-foot"><svg width="13" height="13" viewBox="0 0 24 24" stroke-width="1.9" style="color:var(--muted)"><use href="#i-ic24"/></svg>${o.date} · 经手人 ${o.by}${o.auditor?' · 审核 '+o.auditor:''}</div>
@@ -120,11 +132,11 @@ function actions(o){
return '<div class="btn ghost" style="flex:1;justify-content:center" onclick="closeSheet()">关闭</div>';
}
function openOrder(no){ const o=ORDERS.find(x=>x.no===no);
const pendRow=o.pending?`<div class="drow"><span>售价</span><b><span class="badge b-待定价">含待定价</span></b></div>`:'';
const pendRow=o.pending?`<div class="drow"><span>售价</span><b>${biB('待定价')}</b></div>`:'';
const confirmBtn=(o.pending&&o.status==='已审核')?`<div class="btn primary" style="flex:1;justify-content:center" onclick="openConfirm('${o.no}')"><svg viewBox="0 0 24 24"><use href="#i-edit"/></svg>确认售价</div>`:'';
const lns=o.lines||[];
const lineRows=lns.map((l,i)=>`<div class="drow"${i===lns.length-1?' style="border-bottom:none"':''}><span style="color:var(--text);line-height:1.45">${l.n}<br><span style="color:var(--faint);font-size:var(--fs-xs)">${l.s} · ×${l.q}</span></span><b style="font-family:var(--font-mono)">${l.pend?'<span class="badge b-待定价">待定价</span>':l.a}</b></div>`).join('');
openSheet('出库单详情', `<div class="drow"><span>单号</span><b style="font-family:var(--font-mono)">${o.no}</b></div><div class="drow"><span>客户</span><b>${o.party}</b></div><div class="drow"><span>单据日期</span><b>${o.date}</b></div><div class="drow"><span>出库员</span><b>${o.by}</b></div><div class="drow"><span>审核员</span><b>${o.auditor||'—'}</b></div><div class="drow"><span>合计金额</span><b style="font-family:var(--font-mono)">${o.amount}</b></div><div class="drow"><span>合计利润 · 管理员可见</span><b style="font-family:var(--font-mono);color:var(--success)">${o.profit}</b></div>${pendRow}<div class="drow" style="border-bottom:none"><span>状态</span><b><span class="badge b-${o.status}">${o.status}</span>${o.ret?` <span class="badge b-${o.ret}">${o.ret}</span>`:''}</b></div>
const lineRows=lns.map((l,i)=>`<div class="drow"${i===lns.length-1?' style="border-bottom:none"':''}><span style="color:var(--text);line-height:1.45">${l.n}<br><span style="color:var(--faint);font-size:var(--fs-xs)">${l.s} · ×${l.q}</span></span><b style="font-family:var(--font-mono)">${l.pend?biB('待定价'):l.a}</b></div>`).join('');
openSheet('出库单详情', `<div class="drow"><span>单号</span><b style="font-family:var(--font-mono)">${o.no}</b></div><div class="drow"><span>客户</span><b>${o.party}</b></div><div class="drow"><span>单据日期</span><b>${o.date}</b></div><div class="drow"><span>出库员</span><b>${o.by}</b></div><div class="drow"><span>审核员</span><b>${o.auditor||'—'}</b></div><div class="drow"><span>合计金额</span><b style="font-family:var(--font-mono)">${o.amount}</b></div><div class="drow"><span>合计利润 · 管理员可见</span><b style="font-family:var(--font-mono);color:var(--success)">${o.profit}</b></div>${pendRow}<div class="drow" style="border-bottom:none"><span>状态</span><b>${biB(o.status)}${o.ret?' '+biB(o.ret):''}</b></div>
<div class="pe-card" style="margin-top:14px"><div class="pe-h"><span class="pe-ic"><svg viewBox="0 0 24 24"><use href="#i-ic33"/></svg></span>商品明细 · ${lns.length} 项</div><div class="pe-b" style="padding-top:2px;padding-bottom:2px">${lineRows}</div></div>
${o.pending?'<div style="margin-top:12px;font-size:var(--fs-sm);line-height:1.6;color:var(--warn)">「先卖后定价」:售价待定,价格确定后点「确认售价」按售价×数量结算应收。</div>':''}
<div style="display:flex;gap:10px;margin-top:16px">${confirmBtn}${actions(o)}</div>`);
@@ -154,6 +154,9 @@ const PARTY_LABEL='供应商';
function money(n){ return '¥'+n.toLocaleString(); }
/* ---- 状态代表图标(徽章图标化 2026-07-04:圆点 → .bi 图标徽章) ---- */
const STICON={'草稿':'i-ic06','待审核':'i-ic04','已审核':'i-check','已拒绝':'i-close','部分退单':'i-undo','已退单':'i-undo','待定价':'i-tag'};
function stBadge(v){ return `<span class="badge bi b-${v}"><svg class="ic"><use href="#${STICON[v]}"/></svg>${v}</span>`; }
function numf(n){ return (Number(n)||0).toLocaleString(); } /* 明细行内数字:去¥,缩一号(2026-07-03 */
function filtered(){ return ORDERS.filter(o=>{
if(state.status!=='全部'&&o.status!==state.status&&o.ret!==state.status)return false;
@@ -198,7 +201,7 @@ function render(){
document.getElementById('thead').innerHTML='<tr>'+cols.map(c=>{ const cls=(c.num?'num ':'')+(c.key==='act'?'act ':''); if(c.filter){const on=state.status!=='全部'; return `<th class="${cls}"><span class="fh ${on?'filtered':''}" onclick="openFilter(this)">${c.label} <svg class="funnel" viewBox="0 0 24 24"><use href="#i-ic22"/></svg></span></th>`;} return `<th class="${cls}">${c.label}</th>`; }).join('')+'</tr>';
const rows=filtered(); const start=(state.page-1)*state.perPage; const pageRows=rows.slice(start,start+state.perPage); const tb=document.getElementById('tbody');
if(rows.length===0){ tb.innerHTML=`<tr><td colspan="${cols.length}"><div class="empty">没有匹配的入库单 · 试试调整筛选或搜索</div></td></tr>`; }
else { tb.innerHTML=pageRows.map(o=>{ const idx=ORDERS.indexOf(o); return '<tr class="click" onclick="openDetail('+idx+')">'+cols.map(c=>{ if(c.key==='no')return `<td><span class="pcode" style="font-size:var(--fs-body)">${o.no}</span></td>`; if(c.key==='party')return `<td class="pname">${o.party}</td>`; if(c.key==='wh')return `<td>${o.wh}</td>`; if(c.key==='amount')return `<td class="num">${money(o.amount)}</td>`; if(c.key==='status')return `<td><span class="badge b-${o.status}">${o.status}</span>${o.ret?` <span class="badge b-${o.ret}">${o.ret}</span>`:''}</td>`; if(c.key==='op')return `<td>${o.op}</td>`; if(c.key==='auditor')return `<td>${o.auditor?o.auditor:'<span style="color:var(--faint)">—</span>'}</td>`; if(c.key==='act')return `<td class="act"><div class="row-act" onclick="event.stopPropagation()"><svg viewBox="0 0 24 24" onclick="openDetail(${idx})"><use href="#i-eye"/></svg></div></td>`; return `<td>${o[c.key]}</td>`; }).join('')+'</tr>'; }).join(''); }
else { tb.innerHTML=pageRows.map(o=>{ const idx=ORDERS.indexOf(o); return '<tr class="click" onclick="openDetail('+idx+')">'+cols.map(c=>{ if(c.key==='no')return `<td><span class="pcode" style="font-size:var(--fs-body)">${o.no}</span></td>`; if(c.key==='party')return `<td class="pname">${o.party}</td>`; if(c.key==='wh')return `<td>${o.wh}</td>`; if(c.key==='amount')return `<td class="num">${money(o.amount)}</td>`; if(c.key==='status')return `<td>${stBadge(o.status)}${o.ret?' '+stBadge(o.ret):''}</td>`; if(c.key==='op')return `<td>${o.op}</td>`; if(c.key==='auditor')return `<td>${o.auditor?o.auditor:'<span style="color:var(--faint)">—</span>'}</td>`; if(c.key==='act')return `<td class="act"><div class="row-act" onclick="event.stopPropagation()"><svg viewBox="0 0 24 24" onclick="openDetail(${idx})"><use href="#i-eye"/></svg></div></td>`; return `<td>${o[c.key]}</td>`; }).join('')+'</tr>'; }).join(''); }
document.getElementById('pagerInfo').textContent=`显示 ${rows.length?start+1:0}${Math.min(start+state.perPage,rows.length)},共 ${rows.length}`;
const pages=Math.max(1,Math.ceil(rows.length/state.perPage)); let pg=`<button ${state.page<=1?'disabled':''} onclick="go(${state.page-1})"></button>`; for(let p=1;p<=pages;p++)pg+=`<button class="${p===state.page?'cur':''}" onclick="go(${p})">${p}</button>`; pg+=`<button ${state.page>=pages?'disabled':''} onclick="go(${state.page+1})"></button>`; document.getElementById('pager').innerHTML=pg;
document.getElementById('subInfo').textContent=`本月 ${rows.length} 笔入库单`;
@@ -306,7 +309,7 @@ function openDetail(idx){ const o=ORDERS[idx]; document.getElementById('dTitle')
o.lines.forEach(l=>{ const pi=PINFO[l.name]||{}; const pend=(l.price||0)<=0; const priceC=pend?'<span class="pending-price">待定价</span>':numf(l.price); const amtC=pend?'<span class="pending-price">待定价</span>':numf(l.qty*l.price); lh+=`<div class="lr" style="grid-template-columns:1fr 124px 40px 64px 78px;align-items:start"><span class="lcell"><span class="ln">${l.name}</span><span class="lcode">${pi.code||'—'}</span></span><span class="lcell"><span class="lser">${pi.series||'—'}</span><span class="lspec">${pi.spec||''}</span></span><span style="text-align:right;font-family:var(--font-mono)">${l.qty}</span><span style="text-align:right;font-family:var(--font-mono)">${priceC}</span><span class="amt">${amtC}</span></div>`; }); lh+='</div>';
const pendCount=o.lines.filter(l=>(l.price||0)<=0).length;
const auditor=o.auditor?o.auditor:'<span style="color:var(--faint)">—</span>';
document.getElementById('drawerBody').innerHTML=`<div class="drow"><span>单据日期</span><b>${o.date}</b></div><div class="drow"><span>${PARTY_LABEL}</span><b>${o.party}</b></div><div class="drow"><span>仓库</span><b>${o.wh}</b></div><div class="drow"><span>商品数</span><b>${o.count}</b></div><div class="drow"><span>入库员</span><b>${o.op}</b></div><div class="drow"><span>审核员</span><b>${auditor}</b></div><div class="drow"><span>状态</span><b><span class="badge b-${o.status}">${o.status}</span>${o.ret?` <span class="badge b-${o.ret}">${o.ret}</span>`:''}</b></div><label style="display:block;font-size:var(--fs-sm);color:var(--muted);margin:16px 0 8px;">入库明细</label>${lh}<div class="ltotal">合计金额 <b>${money(o.amount)}</b></div>`+
document.getElementById('drawerBody').innerHTML=`<div class="drow"><span>单据日期</span><b>${o.date}</b></div><div class="drow"><span>${PARTY_LABEL}</span><b>${o.party}</b></div><div class="drow"><span>仓库</span><b>${o.wh}</b></div><div class="drow"><span>商品数</span><b>${o.count}</b></div><div class="drow"><span>入库员</span><b>${o.op}</b></div><div class="drow"><span>审核员</span><b>${auditor}</b></div><div class="drow"><span>状态</span><b>${stBadge(o.status)}${o.ret?' '+stBadge(o.ret):''}</b></div><label style="display:block;font-size:var(--fs-sm);color:var(--muted);margin:16px 0 8px;">入库明细</label>${lh}<div class="ltotal">合计金额 <b>${money(o.amount)}</b></div>`+
`<div class="dactions"><div class="dact-group"><span class="dact-label">单据审核</span><div class="dact-row">${auditBtns(o,idx)}</div></div>${(o.status==='已审核'&&pendCount>0)?`<div class="dact-group"><span class="dact-label">成本 · ${pendCount} 项待定价</span><div class="dact-row"><div class="btn primary sm" onclick="openConfirmCost(${idx})"><svg viewBox="0 0 24 24"><use href="#i-edit"/></svg>确认进价</div></div></div>`:''}<div class="dact-group"><span class="dact-label">打印</span><div class="dact-row"><div class="btn ghost sm" onclick="toast('打印标签(原型)')"><svg viewBox="0 0 24 24"><use href="#i-ic49"/></svg>打印标签</div><div class="btn ghost sm" onclick="toast('打印单据(原型)')"><svg viewBox="0 0 24 24"><use href="#i-ic50"/></svg>打印单据</div></div></div></div>`;
document.getElementById('drawer').classList.add('show'); document.getElementById('drawerMask').classList.add('show'); }
function closeDrawer(){ document.getElementById('drawer').classList.remove('show'); document.getElementById('drawerMask').classList.remove('show'); }
@@ -153,6 +153,9 @@ const PARTY_LABEL='客户';
function money(n){ return '¥'+n.toLocaleString(); }
/* ---- 状态代表图标(徽章图标化 2026-07-04:圆点 → .bi 图标徽章) ---- */
const STICON={'草稿':'i-ic06','待审核':'i-ic04','已审核':'i-check','已拒绝':'i-close','部分退单':'i-undo','已退单':'i-undo','待定价':'i-tag'};
function stBadge(v){ return `<span class="badge bi b-${v}"><svg class="ic"><use href="#${STICON[v]}"/></svg>${v}</span>`; }
function numf(n){ return (Number(n)||0).toLocaleString(); } /* 明细行内数字:去¥,缩一号(2026-07-03 */
function filtered(){ return ORDERS.filter(o=>{
if(state.status!=='全部'&&o.status!==state.status&&o.ret!==state.status)return false;
@@ -197,7 +200,7 @@ function render(){
document.getElementById('thead').innerHTML='<tr>'+cols.map(c=>{ const cls=(c.num?'num ':'')+(c.key==='act'?'act ':''); if(c.filter){const on=state.status!=='全部'; return `<th class="${cls}"><span class="fh ${on?'filtered':''}" onclick="openFilter(this)">${c.label} <svg class="funnel" viewBox="0 0 24 24"><use href="#i-ic22"/></svg></span></th>`;} return `<th class="${cls}">${c.label}</th>`; }).join('')+'</tr>';
const rows=filtered(); const start=(state.page-1)*state.perPage; const pageRows=rows.slice(start,start+state.perPage); const tb=document.getElementById('tbody');
if(rows.length===0){ tb.innerHTML=`<tr><td colspan="${cols.length}"><div class="empty">没有匹配的出库单 · 试试调整筛选或搜索</div></td></tr>`; }
else { tb.innerHTML=pageRows.map(o=>{ const idx=ORDERS.indexOf(o); return '<tr class="click" onclick="openDetail('+idx+')">'+cols.map(c=>{ if(c.key==='no')return `<td><span class="pcode" style="font-size:var(--fs-body)">${o.no}</span></td>`; if(c.key==='party')return `<td class="pname">${o.party}</td>`; if(c.key==='wh')return `<td>${o.wh}</td>`; if(c.key==='amount')return `<td class="num">${money(o.amount)}</td>`; if(c.key==='status')return `<td><span class="badge b-${o.status}">${o.status}</span>${o.ret?` <span class="badge b-${o.ret}">${o.ret}</span>`:''}</td>`; if(c.key==='op')return `<td>${o.op}</td>`; if(c.key==='auditor')return `<td>${o.auditor?o.auditor:'<span style="color:var(--faint)">—</span>'}</td>`; if(c.key==='act')return `<td class="act"><div class="row-act" onclick="event.stopPropagation()"><svg viewBox="0 0 24 24" onclick="openDetail(${idx})"><use href="#i-eye"/></svg></div></td>`; return `<td>${o[c.key]}</td>`; }).join('')+'</tr>'; }).join(''); }
else { tb.innerHTML=pageRows.map(o=>{ const idx=ORDERS.indexOf(o); return '<tr class="click" onclick="openDetail('+idx+')">'+cols.map(c=>{ if(c.key==='no')return `<td><span class="pcode" style="font-size:var(--fs-body)">${o.no}</span></td>`; if(c.key==='party')return `<td class="pname">${o.party}</td>`; if(c.key==='wh')return `<td>${o.wh}</td>`; if(c.key==='amount')return `<td class="num">${money(o.amount)}</td>`; if(c.key==='status')return `<td>${stBadge(o.status)}${o.ret?' '+stBadge(o.ret):''}</td>`; if(c.key==='op')return `<td>${o.op}</td>`; if(c.key==='auditor')return `<td>${o.auditor?o.auditor:'<span style="color:var(--faint)">—</span>'}</td>`; if(c.key==='act')return `<td class="act"><div class="row-act" onclick="event.stopPropagation()"><svg viewBox="0 0 24 24" onclick="openDetail(${idx})"><use href="#i-eye"/></svg></div></td>`; return `<td>${o[c.key]}</td>`; }).join('')+'</tr>'; }).join(''); }
document.getElementById('pagerInfo').textContent=`显示 ${rows.length?start+1:0}${Math.min(start+state.perPage,rows.length)},共 ${rows.length}`;
const pages=Math.max(1,Math.ceil(rows.length/state.perPage)); let pg=`<button ${state.page<=1?'disabled':''} onclick="go(${state.page-1})"></button>`; for(let p=1;p<=pages;p++)pg+=`<button class="${p===state.page?'cur':''}" onclick="go(${p})">${p}</button>`; pg+=`<button ${state.page>=pages?'disabled':''} onclick="go(${state.page+1})"></button>`; document.getElementById('pager').innerHTML=pg;
document.getElementById('subInfo').textContent=`本月 ${rows.length} 笔出库单`;
@@ -302,7 +305,7 @@ function openDetail(idx){ const o=ORDERS[idx]; document.getElementById('dTitle')
o.lines.forEach(l=>{ const pi=PINFO[l.name]||{}; const cost=l.cost!=null?l.cost:Math.round((l.price||0)*0.85); const pend=(l.price||0)<=0; const saleC=pend?'<span class="pending-price">待定价</span>':numf(l.price); const profit=(l.price-cost)*l.qty; const profitC=pend?'<span class="pending-price">待定价</span>':`<span style="color:${profit<0?'var(--danger)':'var(--success)'}">${numf(profit)}</span>`; lh+=`<div class="lr" style="grid-template-columns:${GRID};align-items:start"><span class="lcell"><span class="ln">${l.name}</span><span class="lcode">${pi.code||'—'}</span></span><span class="lcell"><span class="lser">${pi.series||'—'}</span><span class="lspec">${pi.spec||''}</span></span><span style="text-align:right;font-family:var(--font-mono)">${l.qty}</span><span style="text-align:right;font-family:var(--font-mono);font-size:var(--fs-sm)">${numf(cost)}</span><span style="text-align:right;font-family:var(--font-mono);font-size:var(--fs-sm)">${saleC}</span><span class="amt" style="font-size:var(--fs-sm)">${pend?'<span class="pending-price">待定价</span>':numf(l.qty*l.price)}</span><span style="text-align:right;font-family:var(--font-mono);font-size:var(--fs-sm)">${profitC}</span></div>`; }); lh+='</div>';
const pendCount=o.lines.filter(l=>(l.price||0)<=0).length;
const auditor=o.auditor?o.auditor:'<span style="color:var(--faint)">—</span>';
document.getElementById('drawerBody').innerHTML=`<div class="drow"><span>单据日期</span><b>${o.date}</b></div><div class="drow"><span>${PARTY_LABEL}</span><b>${o.party}</b></div><div class="drow"><span>仓库</span><b>${o.wh}</b></div><div class="drow"><span>商品数</span><b>${o.count}</b></div><div class="drow"><span>出库员</span><b>${o.op}</b></div><div class="drow"><span>审核员</span><b>${auditor}</b></div><div class="drow"><span>状态</span><b><span class="badge b-${o.status}">${o.status}</span>${o.ret?` <span class="badge b-${o.ret}">${o.ret}</span>`:''}</b></div><label style="display:block;font-size:var(--fs-sm);color:var(--muted);margin:16px 0 8px;">出库明细</label>${lh}<div class="ltotal">合计金额 <b>${money(o.amount)}</b></div><div class="ltotal" style="padding-top:4px">合计利润 <b style="color:var(--success)">${money(o.lines.reduce((s,l)=>{const c=l.cost!=null?l.cost:Math.round((l.price||0)*0.85);return s+((l.price||0)>0?((l.price-c)*l.qty):0);},0))}</b></div>`+
document.getElementById('drawerBody').innerHTML=`<div class="drow"><span>单据日期</span><b>${o.date}</b></div><div class="drow"><span>${PARTY_LABEL}</span><b>${o.party}</b></div><div class="drow"><span>仓库</span><b>${o.wh}</b></div><div class="drow"><span>商品数</span><b>${o.count}</b></div><div class="drow"><span>出库员</span><b>${o.op}</b></div><div class="drow"><span>审核员</span><b>${auditor}</b></div><div class="drow"><span>状态</span><b>${stBadge(o.status)}${o.ret?' '+stBadge(o.ret):''}</b></div><label style="display:block;font-size:var(--fs-sm);color:var(--muted);margin:16px 0 8px;">出库明细</label>${lh}<div class="ltotal">合计金额 <b>${money(o.amount)}</b></div><div class="ltotal" style="padding-top:4px">合计利润 <b style="color:var(--success)">${money(o.lines.reduce((s,l)=>{const c=l.cost!=null?l.cost:Math.round((l.price||0)*0.85);return s+((l.price||0)>0?((l.price-c)*l.qty):0);},0))}</b></div>`+
`<div class="dactions"><div class="dact-group"><span class="dact-label">单据审核</span><div class="dact-row">${auditBtns(o,idx)}</div></div>${(o.status==='已审核'&&pendCount>0)?`<div class="dact-group"><span class="dact-label">售价 · ${pendCount} 项待定价</span><div class="dact-row"><div class="btn primary sm" onclick="openConfirmSale(${idx})"><svg viewBox="0 0 24 24"><use href="#i-edit"/></svg>确认售价</div></div></div>`:''}<div class="dact-group"><span class="dact-label">打印</span><div class="dact-row"><div class="btn ghost sm" onclick="toast('打印标签(原型)')"><svg viewBox="0 0 24 24"><use href="#i-ic49"/></svg>打印标签</div><div class="btn ghost sm" onclick="toast('打印单据(原型)')"><svg viewBox="0 0 24 24"><use href="#i-ic50"/></svg>打印单据</div></div></div></div>`;
document.getElementById('drawer').classList.add('show'); document.getElementById('drawerMask').classList.add('show'); }
function closeDrawer(){ document.getElementById('drawer').classList.remove('show'); document.getElementById('drawerMask').classList.remove('show'); }
File diff suppressed because one or more lines are too long